1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 15:51:47 -05:00

Merge pull request #2222 from f4exb/feature-wdsp

Feature wdsp
This commit is contained in:
Edouard Griffiths 2024-08-13 15:36:54 +02:00 committed by GitHub
commit 96478f1021
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
161 changed files with 14756 additions and 13191 deletions

View File

@ -6,7 +6,6 @@ on:
push:
branches:
- master
- feature-*
- mac_ci
tags:
- 'v*'

1
.gitignore vendored
View File

@ -28,6 +28,7 @@ obj-x86_64-linux-gnu/*
**/venv*/
*.pyc
.DS_Store
.sonarlint
### Go ###
# Binaries for programs and plugins

BIN
doc/img/WDSPRx_AGC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

BIN
doc/img/WDSPRx_CWpeak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
doc/img/WDSPRx_EQ.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

BIN
doc/img/WDSPRx_NB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

BIN
doc/img/WDSPRx_NB2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

BIN
doc/img/WDSPRx_NR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
doc/img/WDSPRx_NR2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

BIN
doc/img/WDSPRx_RXA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

BIN
doc/img/WDSPRx_plugin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
doc/img/WDSPRx_plugin.xcf Normal file

Binary file not shown.

BIN
doc/img/WDSPRx_plugin_A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
doc/img/WDSPRx_plugin_A.xcf Normal file

Binary file not shown.

BIN
doc/img/WDSPRx_struct.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -45,9 +45,7 @@ class LDPCEncoder
return b < TYPE(0) ? -a : b > TYPE(0) ? a : TYPE(0);
}
public:
LDPCEncoder()
{
}
LDPCEncoder() = default
void init(LDPCInterface *it)
{

View File

@ -0,0 +1,442 @@
<h1>WDSP receiver plugin</h1>
<h2>Introduction</h2>
This plugin implements a modified "RXA unit" of the [WDSP library](https://github.com/TAPR/OpenHPSDR-wdsp) by Warren Pratt, NR0V. This library has been extensively modified to be productively included in SDRangel and can be found in the "wdsp" subdirectory of this repository. However the original DSP algorithms have been preserved and I want to address my thanks to the original author for making it available to the community. Only the code structure was modified to meet Qt and C++ standards. The original version written in C could not benefit of modern C++ features. Also the multithreading support was entirely removed as this is already handled at the upper level in SDRangel. This greatly simplifies the code. Although the details of implementation have been changed the explanation of DSP algorithms found in the [documentation](https://github.com/TAPR/OpenHPSDR-wdsp/blob/master/wdsp%201.24/WDSP%20Guide%2C%20Rev%201.24.pdf) is still valid.
The WDSP library is at the heart of OpenHPSDR and Pi-HPSDR projects and both implement excellent communication receivers. Experience with Pi-HPSDR was the main motivation to create this plugin.
As explained in the documentation the WDSP engine is organized in channels comprising a single unit itself composed of several blocks. In the implementation here only units and blocks were retained and more specifically this plugin makes use of the "RXA" unit. The channel is somehow represented by the channel plugin itself.
![WDSP structure](../../../doc/img/WDSPRx_struct.png)
This "RXA" unit is originally composed of these blocks:
![WDSP RXA](../../../doc/img/WDSPRx_RXA.png)
- Frequency Shifter: used to implement the RIT functionality
- Input Resampler: is not used. The channel interpolator/decimator is used to give a fixed rate of 48 kS/s which is the internal DSP rate of the RXA unit
- Signal Generator: was removed in this implementation
- Input Meter: not used
- Notched Bandpass: this is the main input filter and is permanently active but the notch feature is not used
- S Meter: permanently active this is used for channel power displays
- Post filter display send: permanently active this is the tap providing the spectrum view (B)
- AM Squelch capture: used in AM squelch functionality
- AM/SAM Demodulator: used for AM/SAM modes
- FM Demodulator: used for FM mode
- FM Squelch apply: used in FM squelch functionality
- Spectral Noise blanker: not used
- Equalizer: used in equalizer functionality
- AGC: used in AGC functionality
- Auto Notch Filter: used in ANF functionality
- LMS Noise Reduction: used in noise reduction ¨NR" mode
- Spectral Noise Reduction: used in noise reduction ¨NR2" mode
- Bandpass Filter: participates in the global channel filtering
- AGC Meter: not used
- Scope/Phase display send: not used
- AM Carrier Block: not used
- CW peaking filter: used in CW functionality
- Dolly Filter: not used
- Syllabic Squelch: not displayed in the diagram above. Implements the voice squelch mode mostly useful and efficient in SSB.
- Patch Panel: used for volume and pan controls
- AM Squelch Apply: participates in AM squeclh functionality
- Output resampler: used if the audio sample rate is not 48 kS/s
In addition the noise blanker apply to the complete I/Q stream before it enters the RXA chain described above thus on the full 48 kHz stream. There are two available:
- Preemptive Wideband Noise Blanker described at p.122 of the documentation implements the "NB" noise blanker mode
- Interpolating Wideband Noise Blanker described at p.129 of the documentation implements the "NB2" noise blanker mode
Using this RXA unit this plugin provides something similar to the VFO concept found in other SDR software and hardware radios. It implements the classical amateur radio and SWL audio modes: SSB, AM, FM. CW is not considered a mode apart here. To work CW signals one selects the SSB mode (then USB or LSB) and one may use the CW peaking filter to isolate even more the pitch of interest. The trick to move the displayed frequency on the expected pitch is explained in the RIT section (A.13).
While the advantage over AM and NFM demodulator plugins is debatable this plugin is much more advanced than the SSB demodulator plugin for working SSB and CW signals and generally should be preferred over the SSB demodulator plugin. It retains the SSB demodulator plugin GUI presentation but with better and extended capabilities.
<h2>Interface</h2>
The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md)
Similarly to the SSB demodulator plugin the center of the GUI is divided into the settings (A) and spectrum (B) areas (sections).
![WDSP Rx plugin](../../../doc/img/WDSPRx_plugin.png)
<h2>B: Spectrum</h2>
The spectrum view and controls is similar to other spectrum views. Controls on the bottom of the panel are identical to the ones of the main spectrum display. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md).
This is the spectrum seen after the bandpass filter. The span can be controlled via the span setting (A.11). In LSB mode the frequencies displayed are negative to take into account the spectrum reversal.
<h2>A: Settings</h2>
![WDSP Rx plugin settings](../../../doc/img/WDSPRx_plugin_A.png)
<h3>A.1: Frequency shift from center frequency of reception</h3>
Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mouse wheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
<h3>A.2: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band.
<h3>A.3: Monaural/binaural toggle</h3>
- Monaural: the scalar signal is routed to both left and right audio channels. Note that you should set this mode for the CW decoder feature to work.
- Binaural: the complex signal is fed with the real part on the left audio channel and the imaginary part to the right audio channel.
Right clicking on this button opens a dialog to control audio pan:
![WDSP Rx AM plugin settings](../../../doc/img/WDSPRx_AudioPan_dialog.png)
The "0" button resets the balance to the center. Use the slider to pan the audio more left or more right.
<h3>A.4: Invert left and right channels</h3>
Inverts left and right audio channels. Useful in binaural mode only.
<h3>A.5: Demodulation mode</h3>
Sets the demodulation mode between:
- SSB: for DSB, USB, LSB, CW
- AM: for AM in classical detection mode
- SAM: for AM in synchronous mode
- FM: for narrowband FM
Right clicking on this button opens a dialog to control settings pertaining to the current mode:
**SSB**: no dialog
**AM/SAM**:
![WDSP Rx AM dialog](../../../doc/img/WDSPRx_AM_dialog.png)
There is only one control. This "fade level" when checked substitutes the received carrier with a constant magnitude carrier overcoming possible carrier fades.
**FM**:
![WDSP Rx FM settings](../../../doc/img/WDSPRx_FM_dialog.png)
<h4>1: Expected FM deviation</h4>
Expected FM deviation in kHz. The value appears on the right of the control.
<h4>2: Audio filter low frequency cutoff</h4>
Audio filter low frequency cutoff in kHz. The value appears on the right of the control.
<h4>3: Audio filter high frequency cutoff</h4>
Audio filter high frequency cutoff in kHz. The value appears on the right of the control.
<h4>4: AF limiter</h4>
This is an AGC on the audio signal.
<h4>5: AF limiter gain</h4>
This is the top gain of the audio AGC.
<h4>6: CTCSS notch filter</h4>
This filter can be used to notch out the CTCSS tones. Of course this is useful only if the low frequency cutoff of the audio filter (2) is lower than the tone frequency.
<h4>7: CTCSS notch frequency</h4>
Select the CTCSS frequency to be notched out.
<h3>A.6: Sideband flip</h3>
Flip LSB/USB. Mirror filter bandwidth around zero frequency and change from LSB to USB or vice versa. Works in SSB mode only. This is similar to the same control of the SSB demodulator.
<h3>A.7: SSB/DSB demodulation</h3>
Toggles between SSB (icon with one sideband signal) and DSB (icon with double sidebands signal). In SSB mode the shape of the icon represents LSB or USB operation. This is similar to the same control of the SSB demodulator.
<h3>A.8: Profile selection</h3>
Selects the current profile. A profile keeps track of most of the settings. This allows to rapidly toggle between demodulation modes or between different preferences for the same modulation mode. Up to 10 profiles (0 to 9) can be stored. The current profile number is displayed at the right of the dial button.
<h3>A.9: S points / dB</h3>
Toggles between S points and dB units display for the level meter (A.10)
<h3>A.10: Level meter</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>A.11: Spectrum display frequency span</h3>
The DSP sample rate of 48 kS/s is further decimated by powers of two for the spectrum display and bandpass filter limits. This effectively sets the total available bandwidth depending on the decimation:
- **1** (no decimation): 24 kHz (SSB) or 48 kHz (DSB)
- **2**: 12 kHz (SSB) or 24 kHz (DSB)
- **4**: 6 kHz (SSB) or 12 kHz (DSB)
- **8**: 3 kHz (SSB) or 6 kHz (DSB)
- **16**: 1.5 kHz (SSB) or 3 kHz (DSB)
<h3>A.12: Bandpass FIR filter window</h3>
Controls the window applied to the impulse response of the bandpass filter. According to WDSP documentation you may leave it to "B-H4" which is the 4 term Blackman-Harris window. "B-H7" is the 7 term Blackman-Harris window and is generally an overkill. You have the choice still.
<h3>A.13: Toggles RIT</h3>
Toggles the RIT feature. Internally it uses the "shift" block of the "RXA" unit and hence applies an extra shift over the main frequency shift (A.1) effectively implementing a RIT feature.
You may want to take advantage of the RIT when listening to CW signals. By setting the RIT frequency value (A.14) to the opposite of the desired pitch from the center frequency the displayed frequency and channel marker on the spectrum will fall right on the signal when the proper pitch is obtained. This can greatly facilitate tuning the CW signal at the right pitch which is important if you are using the CW peaking filter (A.23)
For example if the desired pitch is 600 Hz and you are set in USB mode then you would set the RIT frequency to -600 Hz. Conversely if you are set in LSB mode the RIT frequency would be +600 Hz.
When the RIT is engaged the bandwidth display on the spectrum channel marker moves accordingly while the center frequency stays fixed.
<h3>A.14: RIT frequency</h3>
Sets the RIT frequency shift in Hz. The value is displayed at the right of the button.
<h3>A.15: Bandpass filter near frequency cutoff</h3>
Controls the filter cutoff frequency closest to zero with a positive value in USB mode and a negative value in LSB mode. The value is limited by the far frequency cutoff so that it is always lower to the far frequency cutoff in absolute value.
This is effective only for SSB. The value is automatically set to 0 in DSB, AM or FM and control is disabled.
This is similar to the same control in the SSB demodulator.
<h3>A.16: Bandpass filter far frequency cutoff</h3>
Controls the filter cutoff frequency farthest to zero with a positive value in USB mode and a negative value in LSB mode. Moving the slider in the positive range effectively sets the USB mode while moving it in the negative range sets the LSB mode.
One may also use the sideband flip (A.6) to quickly change between USB and LSB keeping the same filter limits.
In DSB, AM and FM it just controls the filter half bandwidth.
This is similar to the same control in the SSB demodulator.
<h3>A.17: Volume control</h3>
Controls the audio volume in dB. The value is displayed at the right of the button.
<h3>A.18: AGC</h3>
Toggles AGC. You would usually leave it always on. You will find details on the AGC algorithm starting p.41 of the WDSP documentation. By right clicking on the button the AGC controls dialog is opened:
![WDSP AGC Dialog](../../../doc/img/WDSPRx_AGC_dialog.png)
<h4>1: AGC mode</h4>
- **Long**: Hang time = 2000 ms. Decay_time_constant = 2000 ms
- **Slow**: Hang time = 1000 ms. Decay_time_constant = 500 ms
- **Medium**: Hang is turned OFF. Decay_time_constant = 250 ms
- **Fast**: Hang is turned OFF. Decay_time_constant = 50 ms
<h4>2: AGC slope</h4>
Adjust the slope in dB. See the diagram next paragraph for details.
<h4>3: Hang threshold</h4>
<H3>A.19: AGC top value</h3>
This is the top value in dB of the gain applied to the signal. You may want to adjust it depending on the conditions to avoid audio signal saturation.
In the following diagram extracted from WDSP documentation the top value is the red line, It is set at 0 dB on the diagram. The actual value is controlled by this button:
![WDSP AGC](../../../doc/img/WDSPRx_AGC.png)
The diagram also shows the effect of the "slope" setting found in the AGC control dialog (A.18)
<h3>A.20: Noise Reduction</h3>
Toggles the noise reduction feature. Details in the WDSP documentation start at p.51 There are 2 possible noise reduction schemes.
Principle of LMS Noise reduction extracted from WDSP documentation:
![WDSP NR](../../../doc/img/WDSPRx_NR.png)
Principe of spectral noise reduction extracted from WDSP documentation:
![WDSP NR2](../../../doc/img/WDSPRx_NR2.png)
Right clicking on this button opens a dialog to control noise reduction settings:
![WDSP AGC](../../../doc/img/WDSPRx_NR_dialog.png)
<h4>1: Noise reduction scheme</h4>
Choice is between "NR" for LMS Noise Reduction and "NR2" for Spectral Noise Reduction.
NR usually works better for CW possibly FM while NR2 works better for SSB and AM/SAM.
<h4>2: NR2 gain per frequency bin calculation</h4>
Applies to spectral noise reduction (NR2). Choice between:
- **Linear**: Gaussian speech distribution, linear amplitude scale
- **Log**: Gaussian speech distribution, log amplitude scale
- **Gamma**: (default) Gamma speech distribution
According to WDSP documentation *"All three choices will produce
somewhat similar results with the default Gamma speech distribution being somewhat preferred."*
<h4>3: NR2 Noise Power Estimation method</h4>
Applies to spectral noise reduction (NR2). Choice between:
- **OSMS**: (default) Optimal Smoothing Minimum Statistics
- **MMSE**: Minimum MeanSquare Error
According to WDSP documentation *"Both choices will produce somewhat similar results. For general operation, the default is preferred. In situation involving sudden changes in signal/noise amplitude, e.g., with "static crashes" caused by lightning, the MMSE method may be preferred due to its ability to more rapidly adjust to the changes."*
<h4>4: Noise reduction position</h4>
Applies to both NR and NR2. Locates the noise reduction block either before or after the AGC is applied.
<h4>5: NR2 artifacts reduction</h4>
Applies to spectral noise reduction (NR2). Turns off/on an artifactelimination postfilter. It is recommended to leave it always on.
<h3>A.21: Noise Blanking</h3>
Toggles the noise blanking feature. Details in the WDSP documentation start at p.122. Choice is between Preemptive Wideband Noise Blanker (NB) and Interpolating Wideband Noise Blanker (NB2)
Principe of the Preemptive Wideband Noise Blanker extracted from WDSP documentation:
![WDSP NB](../../../doc/img/WDSPRx_NB.png)
Principle of the Interpolating Wideband Noise Blanker extracted from WDSP documentation:
![WDSP NB2](../../../doc/img/WDSPRx_NB2.png)
Right clicking on this button opens a dialog to control noise blanking settings:
![WDSP NB dialog](../../../doc/img/WDSPRx_NB_dialog.png)
<h4>1: Noise blanker scheme</h4>
Choice between "NB" or Preemptive Wideband Noise Blanker and "NB2" or Interpolating Wideband Noise Blanker.
<h4>2: NB2 mode</h4>
Determines what estimate values are used for the sequence of corrupt samples to be replaced. Choice between:
- **Zero**: zero mode: estimate as zero
- **Sample&Hold**: take the value of noncorrupt signal at the beginning of the impulse and hold that throughout the corrupt sequence
- **Mean Hold**: average the noncorrupt values at the beginning and end of the corrupt sequence and use that as the estimate during the corrupt sequence
- **Hold Sample**: take the value of noncorrupt signal at the end of the impulse and hold that throughout the corrupt sequence
- **Interpolate**: linearly interpolate across the corrupt sequence
<h4>3: Slew time</h4>
This is the duration of the raisedcosine transitions between normal signal levels and the zero estimate during an impulse (tau)
<h4>4: Lag time</h4>
This is the hang time after the impulse
<h4>5: Averaging time</h4>
This is the timeconstant for averaging power across the wide bandwidth (back tau)
<h4>6: Lead time</h4>
This is the advance time before the impulse
<h4>7: Threshold</h4>
A sample is judged to be part of an impulse if its magnitude is greater than threshold*average_power. If threshold is too low, normal signals may be wrongly identified as impulse noise. If it is too high, successful detection will not occur.
<h3>A.22: Automatic Notch Filter</h3>
Toggles the Automatic Notch Filter. This is useful to eliminate a single tone signal. Of course this is not to be used for CW since it would automatically cancel the CW signal.
<h3>A.23: CW peaking filter</h3>
Toggles the CW peaking filter. The CW Peaking Filter is an audio IIR filter intended to peak a single frequency, the frequency of a
desired CW signal.
This is the diagram extracted from WDSP documentation showing the response of the filter for different bandwidths:
![WDSP CW peaking](../../../doc/img/WDSPRx_CWpeak.png)
Right clicking on this button opens a dialog controlling the details of this filter:
![WDSP CW dialog](../../../doc/img/WDSPRx_CW_dialog.png)
<h4>1: Peak frequency</h4>
Adjust the center frequency (Hz) of the filter. This should correspond to the desired pitch. To center the CW signal on this pitch frequency see RIT (A.13) for a practical method.
<h4>2: Bandwidth</h4>
Bandwidth (at -3 dB) of the filter in Hz.
<h4>4: Gain</h4>
Gain of the filter in linear terms (not dB)
<h3>A.24: Squelch</h3>
Toggles the squelch feature. Right clicking on this button opens a dialog to control squelch details:
![WDSP SQ dialog](../../../doc/img/WDSPRx_SQ_dialog.png)
<h4>1: Squelch type</h4>
These checkboxes let you choose the type of squelch that is applied. It enables the options corresponding to the selected type of squelch (Voice: options 2 and 3, AM option 4).
- **Voice**: This type of squelch is based on identifying voice artifacts to open the squelch. It best applies to SSB.
- **AM**: This is the classical magnitude based squelch. It best applies to AM and CW
- **FM**: This squelch is based on measuring the noise level after the discriminator and opens the squelch if it falls below a certain level. It only applies to FM (of course).
<h4>2: Mute time constant</h4>
For voice squelch this is the time to progressively mute the signal when the squelch closes.
<h4>3: Un-mute time constant</h4>
For voice squelch this is the time to progressively un-mute the signal when the squelch opens.
<h4>4: Maximum tail time</h4>
For AM squelch this is the maximum grace period after the squelch closes to effectively mute the audio.
<h3>A.25: Squelch threshold</h3>
Controls the squelch threshold. The value is a percentage and its effect depends on the type of squelch used (selected by A.23 dialog). Generally the greater the value the higher the threshold. Given &theta; is the factor value from 0.0 (0%) to 1.0 (100%):
- Voice squelch: see `ssql.cpp` WDSP code: $0,75\times\theta$
- AM squelch: magnitude threshold in dB: $160\times\theta - 160$
- FM squelch: squelch opens if noise magnitude falls below this value: $0.9\times10^{-2\theta}$
<h3>A.26: Equalizer</h3>
Toggles a 10 frequency points with continuous response equalizer. This diagram extracted from the WDSP documentation explains the difference with a classical band equalizer:
![WDSP EQ](../../../doc/img/WDSPRx_EQ.png)
Right clicking on this opens a dialog to control equalizer details:
![WDSP EQ dialog](../../../doc/img/WDSPRx_EQ_dialog.png)
<h4>1: Preamplifier gain</h4>
This is in fact the global gain in dB. The value appears at the right of the slider.
<h4>2: Frequencies</h4>
Specify the frequency (Hz) of each of the 10 points. Defaults are at the center of the possible ranges:
- **f1**: from 0 to 48 Hz
- **f2**: from 49 to 94 Hz
- **f3**: from 95 to 187 Hz
- **f4**: from 188 to 375 Hz
- **f5**: from 376 to 750 Hz
- **f6**: from 751 to 1500 Hz
- **f7**: from 1501 to 3000 Hz
- **f8**: from 3001 to 6000 Hz
- **f9**: from 6001 to 12000 Hz
- **f10**: from 12001 to 20000 Hz
<h4>3: Gains</h4>
Specify the gain (dB) at each of the 10 frequency points. The value appears at the right of the slider.

View File

@ -279,6 +279,9 @@ QByteArray WDSPRxSettings::serialize() const
s.writeDouble(163 + 100*i, m_profiles[i].m_ssqlTauMute);
s.writeDouble(164 + 100*i, m_profiles[i].m_ssqlTauUnmute);
s.writeDouble(165 + 100*i, m_profiles[i].m_amsqMaxTail);
// RIT
s.writeBool( 183 + 100*i, m_profiles[i].m_rit);
s.writeDouble(184 + 100*i, m_profiles[i].m_ritFrequency);
// Equalizer
s.writeBool( 190 + 100*i, m_profiles[i].m_equalizer);
s.writeFloat(4100 + 100*i, m_profiles[i].m_eqF[0]);
@ -403,15 +406,15 @@ bool WDSPRxSettings::deserialize(const QByteArray& data)
d.readU32( 74, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
m_reverseAPIPort = (uint16_t) utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32( 75, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : (uint16_t) utmp;
d.readU32( 76, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
m_reverseAPIChannelIndex = utmp > 99 ? 99 : (uint16_t) utmp;
d.readS32( 77, &m_streamIndex, 0);
if (m_rollupState)
@ -464,9 +467,9 @@ bool WDSPRxSettings::deserialize(const QByteArray& data)
// Filter
d.readS32 (100 + 100*i, &m_profiles[i].m_spanLog2, 3);
d.readS32 (101 + 100*i, &tmp, 30);
m_profiles[i].m_highCutoff = tmp * 100.0;
m_profiles[i].m_highCutoff = (float) tmp * 100.0f;
d.readS32 (102 + 100*i, &tmp, 3);
m_profiles[i].m_lowCutoff = tmp * 100.0;
m_profiles[i].m_lowCutoff = (float) tmp * 100.0f;
d.readS32 (103 + 100*i, &m_profiles[i].m_fftWindow, 0);
// AGC
d.readBool( 110 + 100*i, &m_profiles[i].m_agc, true);

View File

@ -26,7 +26,6 @@
#include "util/messagequeue.h"
#include "maincore.h"
#include "RXA.hpp"
#include "nbp.hpp"
#include "meter.hpp"
#include "patchpanel.hpp"
#include "wcpAGC.hpp"
@ -38,12 +37,12 @@
#include "nob.hpp"
#include "amd.hpp"
#include "fmd.hpp"
#include "iir.cpp"
#include "ssql.hpp"
#include "amsq.hpp"
#include "fmsq.hpp"
#include "eq.hpp"
#include "eqp.hpp"
#include "shift.hpp"
#include "speak.hpp"
#include "wdsprxsink.h"
@ -83,10 +82,10 @@ void WDSPRxSink::SpectrumProbe::proceed(const float *in, int nb_samples)
{
if (!(m_undersampleCount++ & decim_mask))
{
float avgr = m_sum.real() / decim;
float avgi = m_sum.imag() / decim;
float avgr = m_sum.real() / (float) decim;
float avgi = m_sum.imag() / (float) decim;
if (!m_dsb & !m_usb)
if (!m_dsb && !m_usb)
{ // invert spectrum for LSB
m_sampleVector.push_back(Sample(avgi*SDR_RX_SCALEF, avgr*SDR_RX_SCALEF));
}
@ -125,14 +124,14 @@ WDSPRxSink::WDSPRxSink() :
m_sPeak = 0.0;
m_sCount = m_wdspBufSize;
m_rxa = WDSP::RXA::create_rxa(
m_rxa = new WDSP::RXA(
m_wdspSampleRate, // input samplerate
m_wdspSampleRate, // output samplerate
m_wdspSampleRate, // sample rate for mainstream dsp processing (dsp)
m_wdspBufSize // number complex samples processed per buffer in mainstream dsp processing
);
m_rxa->setSpectrumProbe(&m_spectrumProbe);
WDSP::RXA::SetPassband(*m_rxa, 0, m_Bandwidth);
m_rxa->setPassband(0, m_Bandwidth);
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
applySettings(m_settings, true);
@ -140,7 +139,7 @@ WDSPRxSink::WDSPRxSink() :
WDSPRxSink::~WDSPRxSink()
{
WDSP::RXA::destroy_rxa(m_rxa);
delete m_rxa;
}
void WDSPRxSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
@ -175,25 +174,25 @@ void WDSPRxSink::feed(const SampleVector::const_iterator& begin, const SampleVec
}
}
void WDSPRxSink::getMagSqLevels(double& avg, double& peak, int& nbSamples)
void WDSPRxSink::getMagSqLevels(double& avg, double& peak, int& nbSamples) const
{
avg = m_sAvg;
peak = m_sPeak;
nbSamples = m_sCount;
}
void WDSPRxSink::processOneSample(Complex &ci)
void WDSPRxSink::processOneSample(const Complex &ci)
{
m_rxa->get_inbuff()[2*m_inCount] = ci.imag() / SDR_RX_SCALEF;
m_rxa->get_inbuff()[2*m_inCount+1] = ci.real() / SDR_RX_SCALEF;
if (++m_inCount == m_rxa->get_insize())
{
WDSP::RXA::xrxa(m_rxa);
m_rxa->execute();
m_sCount = m_wdspBufSize;
m_sAvg = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_AV);
m_sPeak = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_PK);
m_sAvg = m_rxa->smeter->getMeter(WDSP::RXA::RXA_S_AV);
m_sPeak = m_rxa->smeter->getMeter(WDSP::RXA::RXA_S_PK);
for (int i = 0; i < m_rxa->get_outsize(); i++)
{
@ -204,10 +203,10 @@ void WDSPRxSink::processOneSample(Complex &ci)
}
else
{
const double& cr = m_rxa->get_outbuff()[2*i+1];
const double& ci = m_rxa->get_outbuff()[2*i];
qint16 zr = cr * 32768.0;
qint16 zi = ci * 32768.0;
const double& dr = m_rxa->get_outbuff()[2*i+1];
const double& di = m_rxa->get_outbuff()[2*i];
qint16 zr = dr * 32768.0;
qint16 zi = di * 32768.0;
m_audioBuffer[m_audioBufferFill].r = zr;
m_audioBuffer[m_audioBufferFill].l = zi;
@ -219,7 +218,7 @@ void WDSPRxSink::processOneSample(Complex &ci)
else
{
Real demod = (zr + zi) * 0.7;
qint16 sample = (qint16)(demod);
auto sample = (qint16)(demod);
m_demodBuffer[m_demodBufferFill++] = sample;
}
@ -228,13 +227,11 @@ void WDSPRxSink::processOneSample(Complex &ci)
QList<ObjectPipe*> dataPipes;
MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes);
if (dataPipes.size() > 0)
if (!dataPipes.empty())
{
QList<ObjectPipe*>::iterator it = dataPipes.begin();
for (; it != dataPipes.end(); ++it)
for (auto dataPipe : dataPipes)
{
DataFifo *fifo = qobject_cast<DataFifo*>((*it)->m_element);
DataFifo *fifo = qobject_cast<DataFifo*>(dataPipe->m_element);
if (fifo)
{
@ -245,7 +242,7 @@ void WDSPRxSink::processOneSample(Complex &ci)
);
}
}
}
}
m_demodBufferFill = 0;
}
@ -306,7 +303,7 @@ void WDSPRxSink::applyAudioSampleRate(int sampleRate)
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_wdspSampleRate;
WDSP::RXA::setOutputSamplerate(m_rxa, sampleRate);
m_rxa->setOutputSamplerate(sampleRate);
m_audioFifo.setSize(sampleRate);
m_audioSampleRate = sampleRate;
@ -316,7 +313,7 @@ void WDSPRxSink::applyAudioSampleRate(int sampleRate)
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes);
if (pipes.size() > 0)
if (!pipes.empty())
{
for (const auto& pipe : pipes)
{
@ -373,8 +370,8 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
if ((m_settings.m_rit != settings.m_rit) || (m_settings.m_ritFrequency != settings.m_ritFrequency) || force)
{
WDSP::SHIFT::SetShiftFreq(*m_rxa, settings.m_ritFrequency);
WDSP::SHIFT::SetShiftRun(*m_rxa, settings.m_rit ? 1 : 0);
m_rxa->shift->SetFreq(settings.m_ritFrequency);
m_rxa->shift->SetRun(settings.m_rit ? 1 : 0);
}
// Filter and mode
@ -385,8 +382,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
(m_settings.m_demod != settings.m_demod) ||
(m_settings.m_dsb != settings.m_dsb) || force)
{
float band, low, high, fLow, fHigh;
bool usb, dsb;
float band;
float low;
float high;
float fLow;
float fHigh;
bool usb;
bool dsb;
band = settings.m_profiles[settings.m_profileIndex].m_highCutoff;
high = band;
@ -446,34 +448,34 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate;
WDSP::RXA::SetPassband(*m_rxa, fLow, fHigh);
WDSP::NBP::NBPSetWindow(*m_rxa, m_settings.m_profiles[m_settings.m_profileIndex].m_fftWindow);
m_rxa->setPassband(fLow, fHigh);
m_rxa->nbpSetWindow(m_settings.m_profiles[m_settings.m_profileIndex].m_fftWindow);
if (settings.m_demod == WDSPRxProfile::DemodSSB)
{
if (dsb) {
WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_DSB);
m_rxa->setMode(WDSP::RXA::RXA_DSB);
} else {
WDSP::RXA::SetMode(*m_rxa, usb ? WDSP::RXA::RXA_USB : WDSP::RXA::RXA_LSB);
m_rxa->setMode(usb ? WDSP::RXA::RXA_USB : WDSP::RXA::RXA_LSB);
}
}
else if (settings.m_demod == WDSPRxProfile::DemodAM)
{
WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_AM);
m_rxa->setMode(WDSP::RXA::RXA_AM);
}
else if (settings.m_demod == WDSPRxProfile::DemodSAM)
{
WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_SAM);
m_rxa->setMode(WDSP::RXA::RXA_SAM);
if (dsb) {
WDSP::AMD::SetAMDSBMode(*m_rxa, 0);
m_rxa->amd->setSBMode(0);
} else {
WDSP::AMD::SetAMDSBMode(*m_rxa, usb ? 2 : 1);
m_rxa->amd->setSBMode(usb ? 2 : 1);
}
}
else if (settings.m_demod == WDSPRxProfile::DemodFMN)
{
WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_FM);
m_rxa->setMode(WDSP::RXA::RXA_FM);
}
}
@ -486,18 +488,18 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
if ((m_settings.m_dnr != settings.m_dnr)
|| (m_settings.m_nrScheme != settings.m_nrScheme) || force)
{
WDSP::ANR::SetANRRun(*m_rxa, 0);
WDSP::EMNR::SetEMNRRun(*m_rxa, 0);
m_rxa->setANRRun(0);
m_rxa->setEMNRRun(0);
if (settings.m_dnr)
{
switch (settings.m_nrScheme)
{
case WDSPRxProfile::NRSchemeNR:
WDSP::ANR::SetANRRun(*m_rxa, 1);
m_rxa->setANRRun(1);
break;
case WDSPRxProfile::NRSchemeNR2:
WDSP::EMNR::SetEMNRRun(*m_rxa, 1);
m_rxa->setEMNRRun(1);
break;
default:
break;
@ -510,12 +512,12 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
switch (settings.m_nrPosition)
{
case WDSPRxProfile::NRPositionPreAGC:
WDSP::ANR::SetANRPosition(*m_rxa, 0);
WDSP::EMNR::SetEMNRPosition(*m_rxa, 0);
m_rxa->setANRPosition(0);
m_rxa->setEMNRPosition(0);
break;
case WDSPRxProfile::NRPositionPostAGC:
WDSP::ANR::SetANRPosition(*m_rxa, 1);
WDSP::EMNR::SetEMNRPosition(*m_rxa, 1);
m_rxa->setANRPosition(1);
m_rxa->setEMNRPosition(1);
break;
default:
break;
@ -527,13 +529,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
switch (settings.m_nr2Gain)
{
case WDSPRxProfile::NR2GainLinear:
WDSP::EMNR::SetEMNRgainMethod(*m_rxa, 0);
m_rxa->emnr->setGainMethod(0);
break;
case WDSPRxProfile::NR2GainLog:
WDSP::EMNR::SetEMNRgainMethod(*m_rxa, 1);
m_rxa->emnr->setGainMethod(1);
break;
case WDSPRxProfile::NR2GainGamma:
WDSP::EMNR::SetEMNRgainMethod(*m_rxa, 2);
m_rxa->emnr->setGainMethod(2);
break;
default:
break;
@ -545,10 +547,10 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
switch (settings.m_nr2NPE)
{
case WDSPRxProfile::NR2NPEOSMS:
WDSP::EMNR::SetEMNRnpeMethod(*m_rxa, 0);
m_rxa->emnr->setNpeMethod(0);
break;
case WDSPRxProfile::NR2NPEMMSE:
WDSP::EMNR::SetEMNRnpeMethod(*m_rxa, 1);
m_rxa->emnr->setNpeMethod(1);
break;
default:
break;
@ -556,34 +558,34 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
}
if ((m_settings.m_nr2ArtifactReduction != settings.m_nr2ArtifactReduction) || force) {
WDSP::EMNR::SetEMNRaeRun(*m_rxa, settings.m_nr2ArtifactReduction ? 1 : 0);
m_rxa->emnr->setAeRun(settings.m_nr2ArtifactReduction ? 1 : 0);
}
if ((m_settings.m_anf != settings.m_anf) || force) {
WDSP::ANF::SetANFRun(*m_rxa, settings.m_anf ? 1 : 0);
m_rxa->setANFRun(settings.m_anf ? 1 : 0);
}
// Caution: Causes corruption
if ((m_settings.m_snb != settings.m_snb) || force) {
WDSP::SNBA::SetSNBARun(*m_rxa, settings.m_snb ? 1 : 0);
m_rxa->setSNBARun(settings.m_snb ? 1 : 0);
}
// CW Peaking
if ((m_settings.m_cwPeaking != settings.m_cwPeaking) || force) {
WDSP::SPEAK::SetSPCWRun(*m_rxa, settings.m_cwPeaking ? 1 : 0);
m_rxa->speak->setRun(settings.m_cwPeaking ? 1 : 0);
}
if ((m_settings.m_cwPeakFrequency != settings.m_cwPeakFrequency) || force) {
WDSP::SPEAK::SetSPCWFreq(*m_rxa, settings.m_cwPeakFrequency);
m_rxa->speak->setFreq(settings.m_cwPeakFrequency);
}
if ((m_settings.m_cwBandwidth != settings.m_cwBandwidth) || force) {
WDSP::SPEAK::SetSPCWBandwidth(*m_rxa, settings.m_cwBandwidth);
m_rxa->speak->setBandwidth(settings.m_cwBandwidth);
}
if ((m_settings.m_cwGain != settings.m_cwGain) || force) {
WDSP::SPEAK::SetSPCWGain(*m_rxa, settings.m_cwGain);
m_rxa->speak->setGain(settings.m_cwGain);
}
// Noise Blanker
@ -591,18 +593,18 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
if ((m_settings.m_dnb != settings.m_dnb)
|| (m_settings.m_nbScheme != settings.m_nbScheme) || force)
{
WDSP::ANB::SetANBRun(*m_rxa, 0);
WDSP::NOB::SetNOBRun(*m_rxa, 0);
m_rxa->anb->setRun(0);
m_rxa->nob->setRun(0);
if (settings.m_dnb)
{
switch(settings.m_nbScheme)
{
case WDSPRxProfile::NBSchemeNB:
WDSP::ANB::SetANBRun(*m_rxa, 1);
m_rxa->anb->setRun(1);
break;
case WDSPRxProfile::NBSchemeNB2:
WDSP::NOB::SetNOBRun(*m_rxa, 1);
m_rxa->nob->setRun(1);
break;
default:
break;
@ -612,66 +614,66 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
if ((m_settings.m_nbSlewTime != settings.m_nbSlewTime) || force)
{
WDSP::ANB::SetANBTau(*m_rxa, settings.m_nbSlewTime * 0.001);
WDSP::NOB::SetNOBTau(*m_rxa, settings.m_nbSlewTime * 0.001);
m_rxa->anb->setTau(settings.m_nbSlewTime * 0.001);
m_rxa->nob->setTau(settings.m_nbSlewTime * 0.001);
}
if ((m_settings.m_nbLeadTime != settings.m_nbLeadTime) || force)
{
WDSP::ANB::SetANBAdvtime(*m_rxa, settings.m_nbLeadTime * 0.001);
WDSP::NOB::SetNOBAdvtime(*m_rxa, settings.m_nbLeadTime * 0.001);
m_rxa->anb->setAdvtime(settings.m_nbLeadTime * 0.001);
m_rxa->nob->setAdvtime(settings.m_nbLeadTime * 0.001);
}
if ((m_settings.m_nbLagTime != settings.m_nbLagTime) || force)
{
WDSP::ANB::SetANBHangtime(*m_rxa, settings.m_nbLagTime * 0.001);
WDSP::NOB::SetNOBHangtime(*m_rxa, settings.m_nbLagTime * 0.001);
m_rxa->anb->setHangtime(settings.m_nbLagTime * 0.001);
m_rxa->nob->setHangtime(settings.m_nbLagTime * 0.001);
}
if ((m_settings.m_nbThreshold != settings.m_nbThreshold) || force)
{
WDSP::ANB::SetANBThreshold(*m_rxa, settings.m_nbThreshold);
WDSP::NOB::SetNOBThreshold(*m_rxa, settings.m_nbThreshold);
m_rxa->anb->setThreshold(settings.m_nbThreshold);
m_rxa->nob->setThreshold(settings.m_nbThreshold);
}
if ((m_settings.m_nbAvgTime != settings.m_nbAvgTime) || force)
{
WDSP::ANB::SetANBBacktau(*m_rxa, settings.m_nbAvgTime * 0.001);
WDSP::NOB::SetNOBBacktau(*m_rxa, settings.m_nbAvgTime * 0.001);
m_rxa->anb->setBacktau(settings.m_nbAvgTime * 0.001);
m_rxa->nob->setBacktau(settings.m_nbAvgTime * 0.001);
}
// AM option
if ((m_settings.m_amFadeLevel != settings.m_amFadeLevel) || force) {
WDSP::AMD::SetAMDFadeLevel(*m_rxa, settings.m_amFadeLevel);
m_rxa->amd->setFadeLevel(settings.m_amFadeLevel);
}
// FM options
if ((m_settings.m_fmDeviation != settings.m_fmDeviation) || force) {
WDSP::FMD::SetFMDeviation(*m_rxa, settings.m_fmDeviation);
m_rxa->fmd->setDeviation(settings.m_fmDeviation);
}
if ((m_settings.m_fmAFLow != settings.m_fmAFLow)
|| (m_settings.m_fmAFHigh != settings.m_fmAFHigh) || force)
{
WDSP::FMD::SetFMAFFilter(*m_rxa, settings.m_fmAFLow, settings.m_fmAFHigh);
m_rxa->fmd->setAFFilter(settings.m_fmAFLow, settings.m_fmAFHigh);
}
if ((m_settings.m_fmAFLimiter != settings.m_fmAFLimiter) || force) {
WDSP::FMD::SetFMLimRun(*m_rxa, settings.m_fmAFLimiter ? 1 : 0);
m_rxa->fmd->setLimRun(settings.m_fmAFLimiter ? 1 : 0);
}
if ((m_settings.m_fmAFLimiterGain != settings.m_fmAFLimiterGain) || force) {
WDSP::FMD::SetFMLimGain(*m_rxa, settings.m_fmAFLimiterGain);
m_rxa->fmd->setLimGain(settings.m_fmAFLimiterGain);
}
if ((m_settings.m_fmCTCSSNotch != settings.m_fmCTCSSNotch) || force) {
WDSP::FMD::SetCTCSSRun(*m_rxa, settings.m_fmCTCSSNotch ? 1 : 0);
m_rxa->fmd->setCTCSSRun(settings.m_fmCTCSSNotch ? 1 : 0);
}
if ((m_settings.m_fmCTCSSNotchFrequency != settings.m_fmCTCSSNotchFrequency) || force) {
WDSP::FMD::SetCTCSSFreq(*m_rxa, settings.m_fmCTCSSNotchFrequency);
m_rxa->fmd->setCTCSSFreq(settings.m_fmCTCSSNotchFrequency);
}
// Squelch
@ -680,9 +682,9 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
|| (m_settings.m_squelchThreshold != settings.m_squelchThreshold)
|| (m_settings.m_squelchMode != settings.m_squelchMode) || force)
{
WDSP::SSQL::SetSSQLRun(*m_rxa, 0);
WDSP::AMSQ::SetAMSQRun(*m_rxa, 0);
WDSP::FMSQ::SetFMSQRun(*m_rxa, 0);
m_rxa->ssql->setRun(0);
m_rxa->amsq->setRun(0);
m_rxa->fmsq->setRun(0);
if (settings.m_squelch)
{
@ -690,24 +692,24 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
{
case WDSPRxProfile::SquelchModeVoice:
{
WDSP::SSQL::SetSSQLRun(*m_rxa, 1);
m_rxa->ssql->setRun(1);
double threshold = 0.0075 * settings.m_squelchThreshold;
WDSP::SSQL::SetSSQLThreshold(*m_rxa, threshold);
m_rxa->ssql->setThreshold(threshold);
}
break;
case WDSPRxProfile::SquelchModeAM:
{
WDSP::AMSQ::SetAMSQRun(*m_rxa, 1);
m_rxa->amsq->setRun(1);
double threshold = ((settings.m_squelchThreshold / 100.0) * 160.0) - 160.0;
WDSP::AMSQ::SetAMSQThreshold(*m_rxa, threshold);
m_rxa->amsq->setThreshold(threshold);
}
break;
case WDSPRxProfile::SquelchModeFM:
{
WDSP::FMSQ::SetFMSQRun(*m_rxa, 1);
m_rxa->fmsq->setRun(1);
double threshold = pow(10.0, -2.0 * ((double) settings.m_squelchThreshold) / 100.0);
qDebug("WDSPRxSink::applySettings: FM squelch %lf", threshold);
WDSP::FMSQ::SetFMSQThreshold(*m_rxa, threshold);
m_rxa->fmsq->setThreshold(threshold);
}
break;
default:
@ -717,33 +719,33 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
}
if ((m_settings.m_ssqlTauMute != settings.m_ssqlTauMute) || force) {
WDSP::SSQL::SetSSQLTauMute(*m_rxa, settings.m_ssqlTauMute);
m_rxa->ssql->setTauMute(settings.m_ssqlTauMute);
}
if ((m_settings.m_ssqlTauUnmute != settings.m_ssqlTauUnmute) || force) {
WDSP::SSQL::SetSSQLTauUnMute(*m_rxa, settings.m_ssqlTauUnmute);
m_rxa->ssql->setTauUnMute(settings.m_ssqlTauUnmute);
}
if ((m_settings.m_amsqMaxTail != settings.m_amsqMaxTail) || force) {
WDSP::AMSQ::SetAMSQMaxTail(*m_rxa, settings.m_amsqMaxTail);
m_rxa->amsq->setMaxTail(settings.m_amsqMaxTail);
}
// Equalizer
if ((m_settings.m_equalizer != settings.m_equalizer) || force) {
WDSP::EQP::SetEQRun(*m_rxa, settings.m_equalizer ? 1 : 0);
m_rxa->eqp->setRun(settings.m_equalizer ? 1 : 0);
}
if ((m_settings.m_eqF != settings.m_eqF)
|| (m_settings.m_eqG != settings.m_eqG) || force)
{
WDSP::EQP::SetEQProfile(*m_rxa, 10, settings.m_eqF.data(), settings.m_eqG.data());
m_rxa->eqp->setProfile(10, settings.m_eqF.data(), settings.m_eqG.data());
}
// Audio panel
if ((m_settings.m_volume != settings.m_volume) || force) {
WDSP::PANEL::SetPanelGain1(*m_rxa, settings.m_volume);
m_rxa->panel->setGain1(settings.m_volume);
}
if ((m_settings.m_audioBinaural != settings.m_audioBinaural)
@ -752,13 +754,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
{
if (settings.m_audioBinaural)
{
WDSP::PANEL::SetPanelCopy(*m_rxa, settings.m_audioFlipChannels ? 3 : 0);
WDSP::PANEL::SetPanelPan(*m_rxa, settings.m_audioPan);
m_rxa->panel->setCopy(settings.m_audioFlipChannels ? 3 : 0);
m_rxa->panel->setPan(settings.m_audioPan);
}
else
{
WDSP::PANEL::SetPanelCopy(*m_rxa, settings.m_audioFlipChannels ? 2 : 1);
WDSP::PANEL::SetPanelPan(*m_rxa, 0.5);
m_rxa->panel->setCopy(settings.m_audioFlipChannels ? 2 : 1);
m_rxa->panel->setPan(0.5);
}
}
@ -770,46 +772,46 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force)
|| (m_settings.m_agcHangThreshold != settings.m_agcHangThreshold)
|| (m_settings.m_agcGain != settings.m_agcGain) || force)
{
WDSP::WCPAGC::SetAGCSlope(*m_rxa, settings.m_agcSlope); // SetRXAAGCSlope(id, rx->agc_slope);
WDSP::WCPAGC::SetAGCTop(*m_rxa, (float) settings.m_agcGain); // SetRXAAGCTop(id, rx->agc_gain);
m_rxa->agc->setSlope(settings.m_agcSlope);
m_rxa->agc->setTop((float) settings.m_agcGain);
if (settings.m_agc)
{
switch (settings.m_agcMode)
{
case WDSPRxProfile::WDSPRxAGCMode::AGCLong:
WDSP::WCPAGC::SetAGCMode(*m_rxa, 1);
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 2000); // SetRXAAGCHang(id, 2000);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 2000); // SetRXAAGCDecay(id, 2000);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold);
m_rxa->agc->setMode(1);
m_rxa->agc->setAttack(2);
m_rxa->agc->setHang(2000);
m_rxa->agc->setDecay(2000);
m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold);
break;
case WDSPRxProfile::WDSPRxAGCMode::AGCSlow:
WDSP::WCPAGC::SetAGCMode(*m_rxa, 2);
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 1000); // SetRXAAGCHang(id, 1000);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 500); // SetRXAAGCDecay(id, 500);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold);
m_rxa->agc->setMode(2);
m_rxa->agc->setAttack(2);
m_rxa->agc->setHang(1000);
m_rxa->agc->setDecay(500);
m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold);
break;
case WDSPRxProfile::WDSPRxAGCMode::AGCMedium:
WDSP::WCPAGC::SetAGCMode(*m_rxa, 3);
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 0); // SetRXAAGCHang(id, 0);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 250); // SetRXAAGCDecay(id, 250);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100);
m_rxa->agc->setMode(3);
m_rxa->agc->setAttack(2);
m_rxa->agc->setHang(0);
m_rxa->agc->setDecay(250);
m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold);
break;
case WDSPRxProfile::WDSPRxAGCMode::AGCFast:
WDSP::WCPAGC::SetAGCMode(*m_rxa, 4);
WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2);
WDSP::WCPAGC::SetAGCHang(*m_rxa, 0); // SetRXAAGCHang(id, 0);
WDSP::WCPAGC::SetAGCDecay(*m_rxa, 50); // SetRXAAGCDecay(id, 50);
WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100);
m_rxa->agc->setMode(4);
m_rxa->agc->setAttack(2);
m_rxa->agc->setHang(0);
m_rxa->agc->setDecay(50);
m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold);
break;
}
}
else
{
WDSP::WCPAGC::SetAGCMode(*m_rxa, 0);
m_rxa->agc->setMode(0);
}
}

View File

@ -55,13 +55,14 @@ public:
bool getAudioActive() const { return m_audioActive; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples);
void getMagSqLevels(double& avg, double& peak, int& nbSamples) const;
private:
class SpectrumProbe : public WDSP::BufferProbe
{
public:
SpectrumProbe(SampleVector& sampleVector);
explicit SpectrumProbe(SampleVector& sampleVector);
virtual ~SpectrumProbe() = default;
virtual void proceed(const float *in, int nbSamples);
void setSpanLog2(int spanLog2);
void setDSB(bool dsb) { m_dsb = dsb; }
@ -102,8 +103,6 @@ private:
Interpolator m_interpolator;
Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain;
// fftfilt* SSBFilter;
// fftfilt* DSBFilter;
SpectrumVis* m_spectrumSink;
SampleVector m_sampleBuffer;
@ -123,7 +122,7 @@ private:
static const int m_wdspSampleRate;
static const int m_wdspBufSize;
void processOneSample(Complex &ci);
void processOneSample(const Complex &ci);
};
#endif // INCLUDE_SSBDEMODSINK_H

View File

@ -69,6 +69,9 @@ QHash<QString, const float *> ColorMap::m_colorMaps{
{"Shrimp", &m_shrimp[0]},
{"Cubehelix", &m_cubehelix[0]},
{"Cubegamma", &m_cubegamma[0]},
{"Cubehlx2", &m_cubehlx2[0]},
{"Icy", &m_icy[0]},
{"Mint", &m_mint[0]},
};
const float ColorMap::m_angel[m_size] =
@ -6313,3 +6316,789 @@ const float ColorMap::m_cubegamma[m_size] =
1.000, 0.998, 0.999,
1.000, 1.000, 1.000,
};
// Generated with https://people.phy.cam.ac.uk/dag9/CUBEHELIX/cubetry.html
// Using: Start color: 1.5, Number of rotations: -1.0, Rotation direction: negative, Hue: 1.0, Gamma: 0.7
const float ColorMap::m_cubehlx2[m_size] =
{
0.000, 0.000, 0.000,
0.023, 0.023, 0.001,
0.037, 0.038, 0.002,
0.051, 0.049, 0.003,
0.063, 0.060, 0.004,
0.075, 0.069, 0.005,
0.086, 0.078, 0.007,
0.098, 0.086, 0.009,
0.109, 0.093, 0.010,
0.120, 0.100, 0.013,
0.131, 0.107, 0.015,
0.141, 0.113, 0.017,
0.152, 0.119, 0.020,
0.163, 0.124, 0.022,
0.173, 0.129, 0.025,
0.184, 0.134, 0.028,
0.195, 0.139, 0.032,
0.205, 0.144, 0.035,
0.216, 0.148, 0.039,
0.226, 0.152, 0.043,
0.237, 0.156, 0.047,
0.247, 0.160, 0.051,
0.257, 0.164, 0.055,
0.267, 0.167, 0.060,
0.278, 0.171, 0.065,
0.288, 0.174, 0.070,
0.298, 0.177, 0.075,
0.308, 0.181, 0.080,
0.318, 0.184, 0.085,
0.328, 0.186, 0.091,
0.337, 0.189, 0.097,
0.347, 0.192, 0.103,
0.357, 0.195, 0.109,
0.366, 0.197, 0.116,
0.375, 0.200, 0.122,
0.385, 0.202, 0.129,
0.394, 0.205, 0.136,
0.403, 0.207, 0.143,
0.412, 0.210, 0.150,
0.421, 0.212, 0.158,
0.429, 0.214, 0.165,
0.438, 0.217, 0.173,
0.446, 0.219, 0.181,
0.454, 0.221, 0.189,
0.462, 0.224, 0.197,
0.470, 0.226, 0.205,
0.478, 0.228, 0.214,
0.486, 0.230, 0.222,
0.493, 0.233, 0.231,
0.501, 0.235, 0.239,
0.508, 0.237, 0.248,
0.515, 0.240, 0.257,
0.522, 0.242, 0.266,
0.528, 0.244, 0.276,
0.535, 0.247, 0.285,
0.541, 0.249, 0.294,
0.547, 0.252, 0.304,
0.553, 0.254, 0.313,
0.559, 0.257, 0.323,
0.565, 0.259, 0.332,
0.570, 0.262, 0.342,
0.575, 0.265, 0.352,
0.580, 0.267, 0.362,
0.585, 0.270, 0.372,
0.590, 0.273, 0.381,
0.594, 0.276, 0.391,
0.599, 0.279, 0.401,
0.603, 0.282, 0.411,
0.607, 0.285, 0.421,
0.610, 0.288, 0.431,
0.614, 0.291, 0.441,
0.617, 0.295, 0.451,
0.621, 0.298, 0.461,
0.624, 0.301, 0.471,
0.626, 0.305, 0.481,
0.629, 0.308, 0.491,
0.632, 0.312, 0.500,
0.634, 0.316, 0.510,
0.636, 0.319, 0.520,
0.638, 0.323, 0.530,
0.640, 0.327, 0.539,
0.641, 0.331, 0.549,
0.643, 0.335, 0.558,
0.644, 0.339, 0.568,
0.645, 0.343, 0.577,
0.646, 0.348, 0.586,
0.647, 0.352, 0.595,
0.648, 0.356, 0.604,
0.648, 0.361, 0.613,
0.649, 0.365, 0.622,
0.649, 0.370, 0.631,
0.649, 0.375, 0.639,
0.649, 0.380, 0.648,
0.649, 0.384, 0.656,
0.648, 0.389, 0.665,
0.648, 0.394, 0.673,
0.648, 0.399, 0.681,
0.647, 0.404, 0.688,
0.646, 0.409, 0.696,
0.645, 0.415, 0.704,
0.644, 0.420, 0.711,
0.643, 0.425, 0.718,
0.642, 0.431, 0.726,
0.641, 0.436, 0.732,
0.640, 0.442, 0.739,
0.638, 0.447, 0.746,
0.637, 0.453, 0.752,
0.635, 0.458, 0.759,
0.634, 0.464, 0.765,
0.632, 0.470, 0.771,
0.630, 0.476, 0.776,
0.629, 0.481, 0.782,
0.627, 0.487, 0.787,
0.625, 0.493, 0.793,
0.623, 0.499, 0.798,
0.621, 0.505, 0.803,
0.620, 0.511, 0.807,
0.618, 0.517, 0.812,
0.616, 0.523, 0.816,
0.614, 0.529, 0.821,
0.612, 0.535, 0.825,
0.610, 0.541, 0.828,
0.608, 0.547, 0.832,
0.606, 0.553, 0.836,
0.604, 0.560, 0.839,
0.602, 0.566, 0.842,
0.601, 0.572, 0.845,
0.599, 0.578, 0.848,
0.597, 0.584, 0.850,
0.595, 0.590, 0.853,
0.594, 0.596, 0.855,
0.592, 0.602, 0.857,
0.591, 0.609, 0.859,
0.589, 0.615, 0.861,
0.588, 0.621, 0.862,
0.586, 0.627, 0.864,
0.585, 0.633, 0.865,
0.584, 0.639, 0.866,
0.582, 0.645, 0.867,
0.581, 0.651, 0.868,
0.580, 0.657, 0.869,
0.579, 0.663, 0.870,
0.579, 0.669, 0.870,
0.578, 0.674, 0.870,
0.577, 0.680, 0.871,
0.577, 0.686, 0.871,
0.576, 0.692, 0.871,
0.576, 0.697, 0.870,
0.576, 0.703, 0.870,
0.576, 0.709, 0.870,
0.576, 0.714, 0.869,
0.576, 0.720, 0.869,
0.576, 0.725, 0.868,
0.577, 0.731, 0.867,
0.577, 0.736, 0.866,
0.578, 0.741, 0.865,
0.578, 0.746, 0.864,
0.579, 0.751, 0.863,
0.580, 0.757, 0.862,
0.581, 0.762, 0.861,
0.583, 0.767, 0.860,
0.584, 0.771, 0.858,
0.586, 0.776, 0.857,
0.587, 0.781, 0.855,
0.589, 0.786, 0.854,
0.591, 0.790, 0.852,
0.593, 0.795, 0.851,
0.595, 0.799, 0.849,
0.597, 0.804, 0.848,
0.600, 0.808, 0.846,
0.602, 0.812, 0.845,
0.605, 0.817, 0.843,
0.608, 0.821, 0.841,
0.611, 0.825, 0.840,
0.614, 0.829, 0.838,
0.617, 0.832, 0.837,
0.620, 0.836, 0.835,
0.624, 0.840, 0.834,
0.627, 0.844, 0.832,
0.631, 0.847, 0.831,
0.635, 0.851, 0.829,
0.639, 0.854, 0.828,
0.643, 0.858, 0.827,
0.647, 0.861, 0.825,
0.651, 0.864, 0.824,
0.655, 0.867, 0.823,
0.660, 0.870, 0.822,
0.664, 0.873, 0.821,
0.669, 0.876, 0.820,
0.674, 0.879, 0.819,
0.679, 0.882, 0.818,
0.683, 0.885, 0.818,
0.688, 0.887, 0.817,
0.693, 0.890, 0.817,
0.699, 0.892, 0.816,
0.704, 0.895, 0.816,
0.709, 0.897, 0.816,
0.714, 0.899, 0.816,
0.720, 0.902, 0.815,
0.725, 0.904, 0.816,
0.731, 0.906, 0.816,
0.736, 0.908, 0.816,
0.742, 0.910, 0.816,
0.748, 0.912, 0.817,
0.753, 0.914, 0.818,
0.759, 0.916, 0.818,
0.765, 0.918, 0.819,
0.771, 0.920, 0.820,
0.776, 0.922, 0.821,
0.782, 0.923, 0.823,
0.788, 0.925, 0.824,
0.794, 0.927, 0.826,
0.800, 0.928, 0.827,
0.806, 0.930, 0.829,
0.811, 0.932, 0.831,
0.817, 0.933, 0.833,
0.823, 0.935, 0.835,
0.829, 0.936, 0.837,
0.835, 0.938, 0.840,
0.840, 0.939, 0.842,
0.846, 0.941, 0.845,
0.852, 0.942, 0.848,
0.857, 0.944, 0.851,
0.863, 0.945, 0.854,
0.869, 0.946, 0.857,
0.874, 0.948, 0.860,
0.880, 0.949, 0.863,
0.885, 0.951, 0.867,
0.890, 0.952, 0.871,
0.896, 0.954, 0.874,
0.901, 0.955, 0.878,
0.906, 0.956, 0.882,
0.911, 0.958, 0.886,
0.916, 0.959, 0.890,
0.921, 0.961, 0.894,
0.926, 0.962, 0.899,
0.930, 0.964, 0.903,
0.935, 0.966, 0.908,
0.940, 0.967, 0.912,
0.944, 0.969, 0.917,
0.948, 0.970, 0.922,
0.953, 0.972, 0.927,
0.957, 0.974, 0.932,
0.961, 0.976, 0.937,
0.965, 0.977, 0.942,
0.968, 0.979, 0.947,
0.972, 0.981, 0.952,
0.976, 0.983, 0.957,
0.979, 0.985, 0.962,
0.983, 0.987, 0.968,
0.986, 0.989, 0.973,
0.989, 0.991, 0.978,
0.992, 0.993, 0.984,
0.995, 0.995, 0.989,
0.997, 0.998, 0.995,
1.000, 1.000, 1.000,
};
// Generated with https://people.phy.cam.ac.uk/dag9/CUBEHELIX/cubetry.html
// Using: Start color: 3.0, Number of rotations: -0.5, Rotation direction: negative, Hue: 1.0, Gamma: 0.7
const float ColorMap::m_icy[m_size] =
{
0.000, 0.000, 0.000,
0.019, 0.018, 0.041,
0.030, 0.029, 0.066,
0.040, 0.039, 0.087,
0.048, 0.048, 0.105,
0.056, 0.057, 0.123,
0.063, 0.065, 0.139,
0.070, 0.073, 0.154,
0.076, 0.080, 0.168,
0.081, 0.088, 0.182,
0.087, 0.095, 0.195,
0.092, 0.103, 0.207,
0.096, 0.110, 0.219,
0.101, 0.117, 0.231,
0.105, 0.124, 0.242,
0.110, 0.130, 0.253,
0.113, 0.137, 0.263,
0.117, 0.144, 0.273,
0.121, 0.151, 0.283,
0.124, 0.157, 0.293,
0.128, 0.164, 0.302,
0.131, 0.171, 0.311,
0.134, 0.177, 0.320,
0.137, 0.184, 0.329,
0.140, 0.190, 0.337,
0.143, 0.196, 0.345,
0.146, 0.203, 0.353,
0.148, 0.209, 0.361,
0.151, 0.216, 0.369,
0.153, 0.222, 0.376,
0.156, 0.228, 0.383,
0.158, 0.235, 0.390,
0.160, 0.241, 0.397,
0.162, 0.247, 0.404,
0.165, 0.253, 0.410,
0.167, 0.260, 0.417,
0.169, 0.266, 0.423,
0.171, 0.272, 0.429,
0.173, 0.278, 0.435,
0.175, 0.284, 0.441,
0.177, 0.290, 0.446,
0.179, 0.297, 0.452,
0.180, 0.303, 0.457,
0.182, 0.309, 0.462,
0.184, 0.315, 0.467,
0.186, 0.321, 0.472,
0.188, 0.327, 0.477,
0.189, 0.333, 0.481,
0.191, 0.339, 0.486,
0.193, 0.345, 0.490,
0.194, 0.351, 0.495,
0.196, 0.357, 0.499,
0.198, 0.362, 0.503,
0.200, 0.368, 0.507,
0.201, 0.374, 0.511,
0.203, 0.380, 0.515,
0.205, 0.386, 0.518,
0.206, 0.392, 0.522,
0.208, 0.397, 0.525,
0.210, 0.403, 0.529,
0.212, 0.409, 0.532,
0.213, 0.415, 0.535,
0.215, 0.420, 0.538,
0.217, 0.426, 0.541,
0.219, 0.431, 0.544,
0.220, 0.437, 0.547,
0.222, 0.443, 0.549,
0.224, 0.448, 0.552,
0.226, 0.454, 0.554,
0.228, 0.459, 0.557,
0.230, 0.465, 0.559,
0.232, 0.470, 0.561,
0.234, 0.475, 0.564,
0.236, 0.481, 0.566,
0.238, 0.486, 0.568,
0.240, 0.491, 0.570,
0.242, 0.497, 0.572,
0.244, 0.502, 0.574,
0.246, 0.507, 0.575,
0.248, 0.512, 0.577,
0.251, 0.518, 0.579,
0.253, 0.523, 0.580,
0.255, 0.528, 0.582,
0.257, 0.533, 0.583,
0.260, 0.538, 0.585,
0.262, 0.543, 0.586,
0.265, 0.548, 0.587,
0.267, 0.553, 0.589,
0.270, 0.558, 0.590,
0.272, 0.563, 0.591,
0.275, 0.567, 0.592,
0.278, 0.572, 0.593,
0.280, 0.577, 0.594,
0.283, 0.582, 0.595,
0.286, 0.586, 0.596,
0.289, 0.591, 0.597,
0.291, 0.596, 0.598,
0.294, 0.600, 0.599,
0.297, 0.605, 0.600,
0.300, 0.609, 0.600,
0.303, 0.614, 0.601,
0.306, 0.618, 0.602,
0.309, 0.623, 0.603,
0.313, 0.627, 0.603,
0.316, 0.631, 0.604,
0.319, 0.636, 0.604,
0.322, 0.640, 0.605,
0.326, 0.644, 0.606,
0.329, 0.648, 0.606,
0.333, 0.653, 0.607,
0.336, 0.657, 0.607,
0.340, 0.661, 0.608,
0.343, 0.665, 0.608,
0.347, 0.669, 0.609,
0.351, 0.673, 0.609,
0.354, 0.677, 0.610,
0.358, 0.681, 0.610,
0.362, 0.685, 0.611,
0.366, 0.688, 0.611,
0.370, 0.692, 0.612,
0.374, 0.696, 0.612,
0.378, 0.700, 0.612,
0.382, 0.703, 0.613,
0.386, 0.707, 0.613,
0.390, 0.711, 0.614,
0.394, 0.714, 0.614,
0.398, 0.718, 0.615,
0.402, 0.721, 0.615,
0.407, 0.725, 0.616,
0.411, 0.728, 0.616,
0.415, 0.731, 0.617,
0.420, 0.735, 0.617,
0.424, 0.738, 0.618,
0.429, 0.741, 0.619,
0.433, 0.744, 0.619,
0.438, 0.748, 0.620,
0.443, 0.751, 0.620,
0.447, 0.754, 0.621,
0.452, 0.757, 0.622,
0.457, 0.760, 0.622,
0.461, 0.763, 0.623,
0.466, 0.766, 0.624,
0.471, 0.769, 0.625,
0.476, 0.772, 0.625,
0.481, 0.775, 0.626,
0.486, 0.778, 0.627,
0.491, 0.781, 0.628,
0.496, 0.783, 0.629,
0.501, 0.786, 0.630,
0.506, 0.789, 0.631,
0.511, 0.792, 0.632,
0.516, 0.794, 0.633,
0.521, 0.797, 0.634,
0.526, 0.799, 0.635,
0.531, 0.802, 0.636,
0.536, 0.805, 0.638,
0.542, 0.807, 0.639,
0.547, 0.810, 0.640,
0.552, 0.812, 0.642,
0.557, 0.815, 0.643,
0.563, 0.817, 0.644,
0.568, 0.819, 0.646,
0.573, 0.822, 0.647,
0.578, 0.824, 0.649,
0.584, 0.826, 0.651,
0.589, 0.829, 0.652,
0.595, 0.831, 0.654,
0.600, 0.833, 0.656,
0.605, 0.835, 0.657,
0.611, 0.837, 0.659,
0.616, 0.840, 0.661,
0.622, 0.842, 0.663,
0.627, 0.844, 0.665,
0.632, 0.846, 0.667,
0.638, 0.848, 0.669,
0.643, 0.850, 0.671,
0.649, 0.852, 0.674,
0.654, 0.854, 0.676,
0.660, 0.856, 0.678,
0.665, 0.858, 0.680,
0.671, 0.860, 0.683,
0.676, 0.862, 0.685,
0.681, 0.864, 0.688,
0.687, 0.866, 0.690,
0.692, 0.868, 0.693,
0.698, 0.869, 0.696,
0.703, 0.871, 0.698,
0.709, 0.873, 0.701,
0.714, 0.875, 0.704,
0.719, 0.877, 0.707,
0.725, 0.879, 0.710,
0.730, 0.880, 0.713,
0.735, 0.882, 0.716,
0.741, 0.884, 0.719,
0.746, 0.886, 0.722,
0.751, 0.887, 0.725,
0.757, 0.889, 0.729,
0.762, 0.891, 0.732,
0.767, 0.893, 0.735,
0.772, 0.894, 0.739,
0.778, 0.896, 0.742,
0.783, 0.898, 0.746,
0.788, 0.900, 0.749,
0.793, 0.901, 0.753,
0.798, 0.903, 0.757,
0.803, 0.905, 0.760,
0.808, 0.906, 0.764,
0.813, 0.908, 0.768,
0.818, 0.910, 0.772,
0.823, 0.911, 0.776,
0.828, 0.913, 0.780,
0.833, 0.915, 0.784,
0.838, 0.917, 0.788,
0.842, 0.918, 0.792,
0.847, 0.920, 0.796,
0.852, 0.922, 0.801,
0.857, 0.923, 0.805,
0.861, 0.925, 0.809,
0.866, 0.927, 0.814,
0.870, 0.929, 0.818,
0.875, 0.930, 0.822,
0.879, 0.932, 0.827,
0.884, 0.934, 0.832,
0.888, 0.936, 0.836,
0.892, 0.937, 0.841,
0.897, 0.939, 0.845,
0.901, 0.941, 0.850,
0.905, 0.943, 0.855,
0.909, 0.945, 0.860,
0.913, 0.946, 0.865,
0.917, 0.948, 0.869,
0.921, 0.950, 0.874,
0.925, 0.952, 0.879,
0.929, 0.954, 0.884,
0.933, 0.956, 0.889,
0.937, 0.958, 0.894,
0.941, 0.960, 0.899,
0.944, 0.962, 0.904,
0.948, 0.964, 0.910,
0.951, 0.965, 0.915,
0.955, 0.967, 0.920,
0.958, 0.970, 0.925,
0.962, 0.972, 0.930,
0.965, 0.974, 0.936,
0.968, 0.976, 0.941,
0.971, 0.978, 0.946,
0.975, 0.980, 0.951,
0.978, 0.982, 0.957,
0.981, 0.984, 0.962,
0.984, 0.986, 0.967,
0.986, 0.989, 0.973,
0.989, 0.991, 0.978,
0.992, 0.993, 0.984,
0.995, 0.995, 0.989,
0.997, 0.998, 0.995,
1.000, 1.000, 1.000,
};
// Generated with https://people.phy.cam.ac.uk/dag9/CUBEHELIX/cubetry.html
// Using: Start color: 2.75, Number of rotations: -0.5, Rotation direction: negative, Hue: 1.0, Gamma: 0.7
const float ColorMap::m_mint[m_size] =
{
0.000, 0.000, 0.000,
0.010, 0.023, 0.038,
0.016, 0.037, 0.061,
0.022, 0.050, 0.080,
0.026, 0.061, 0.097,
0.031, 0.071, 0.113,
0.035, 0.081, 0.127,
0.038, 0.091, 0.141,
0.042, 0.100, 0.153,
0.045, 0.109, 0.165,
0.048, 0.118, 0.177,
0.051, 0.127, 0.188,
0.054, 0.135, 0.198,
0.057, 0.143, 0.208,
0.060, 0.151, 0.217,
0.062, 0.159, 0.227,
0.065, 0.167, 0.235,
0.067, 0.175, 0.244,
0.069, 0.183, 0.252,
0.072, 0.190, 0.260,
0.074, 0.198, 0.267,
0.076, 0.205, 0.275,
0.078, 0.213, 0.282,
0.081, 0.220, 0.289,
0.083, 0.227, 0.295,
0.085, 0.234, 0.302,
0.087, 0.241, 0.308,
0.089, 0.248, 0.314,
0.091, 0.255, 0.320,
0.093, 0.262, 0.325,
0.095, 0.269, 0.331,
0.097, 0.276, 0.336,
0.099, 0.282, 0.341,
0.101, 0.289, 0.346,
0.103, 0.296, 0.351,
0.105, 0.302, 0.356,
0.107, 0.309, 0.360,
0.109, 0.315, 0.364,
0.111, 0.322, 0.369,
0.113, 0.328, 0.373,
0.115, 0.335, 0.377,
0.117, 0.341, 0.380,
0.119, 0.347, 0.384,
0.121, 0.354, 0.388,
0.124, 0.360, 0.391,
0.126, 0.366, 0.394,
0.128, 0.372, 0.398,
0.130, 0.378, 0.401,
0.132, 0.384, 0.404,
0.135, 0.390, 0.407,
0.137, 0.396, 0.409,
0.139, 0.402, 0.412,
0.141, 0.408, 0.415,
0.144, 0.413, 0.417,
0.146, 0.419, 0.420,
0.149, 0.425, 0.422,
0.151, 0.431, 0.424,
0.154, 0.436, 0.426,
0.156, 0.442, 0.428,
0.159, 0.447, 0.430,
0.162, 0.453, 0.432,
0.164, 0.458, 0.434,
0.167, 0.464, 0.436,
0.170, 0.469, 0.438,
0.172, 0.474, 0.439,
0.175, 0.480, 0.441,
0.178, 0.485, 0.443,
0.181, 0.490, 0.444,
0.184, 0.495, 0.446,
0.187, 0.500, 0.447,
0.190, 0.505, 0.448,
0.193, 0.510, 0.450,
0.196, 0.515, 0.451,
0.200, 0.520, 0.452,
0.203, 0.525, 0.453,
0.206, 0.530, 0.454,
0.210, 0.535, 0.455,
0.213, 0.540, 0.456,
0.216, 0.544, 0.457,
0.220, 0.549, 0.458,
0.223, 0.554, 0.459,
0.227, 0.558, 0.460,
0.231, 0.563, 0.461,
0.234, 0.567, 0.462,
0.238, 0.572, 0.463,
0.242, 0.576, 0.463,
0.246, 0.581, 0.464,
0.249, 0.585, 0.465,
0.253, 0.589, 0.466,
0.257, 0.593, 0.466,
0.261, 0.598, 0.467,
0.265, 0.602, 0.468,
0.270, 0.606, 0.469,
0.274, 0.610, 0.469,
0.278, 0.614, 0.470,
0.282, 0.618, 0.471,
0.286, 0.622, 0.471,
0.291, 0.626, 0.472,
0.295, 0.630, 0.473,
0.300, 0.633, 0.473,
0.304, 0.637, 0.474,
0.308, 0.641, 0.475,
0.313, 0.645, 0.475,
0.318, 0.648, 0.476,
0.322, 0.652, 0.477,
0.327, 0.655, 0.478,
0.332, 0.659, 0.478,
0.336, 0.663, 0.479,
0.341, 0.666, 0.480,
0.346, 0.669, 0.480,
0.351, 0.673, 0.481,
0.356, 0.676, 0.482,
0.361, 0.679, 0.483,
0.366, 0.683, 0.484,
0.371, 0.686, 0.485,
0.376, 0.689, 0.485,
0.381, 0.692, 0.486,
0.386, 0.695, 0.487,
0.391, 0.698, 0.488,
0.396, 0.701, 0.489,
0.402, 0.704, 0.490,
0.407, 0.707, 0.491,
0.412, 0.710, 0.492,
0.417, 0.713, 0.493,
0.423, 0.716, 0.495,
0.428, 0.719, 0.496,
0.434, 0.722, 0.497,
0.439, 0.724, 0.498,
0.444, 0.727, 0.499,
0.450, 0.730, 0.501,
0.455, 0.732, 0.502,
0.461, 0.735, 0.504,
0.466, 0.738, 0.505,
0.472, 0.740, 0.506,
0.478, 0.743, 0.508,
0.483, 0.745, 0.509,
0.489, 0.748, 0.511,
0.494, 0.750, 0.513,
0.500, 0.753, 0.514,
0.506, 0.755, 0.516,
0.511, 0.757, 0.518,
0.517, 0.760, 0.520,
0.523, 0.762, 0.522,
0.528, 0.764, 0.523,
0.534, 0.767, 0.525,
0.540, 0.769, 0.527,
0.545, 0.771, 0.529,
0.551, 0.773, 0.532,
0.557, 0.775, 0.534,
0.563, 0.778, 0.536,
0.568, 0.780, 0.538,
0.574, 0.782, 0.540,
0.580, 0.784, 0.543,
0.586, 0.786, 0.545,
0.591, 0.788, 0.548,
0.597, 0.790, 0.550,
0.603, 0.792, 0.553,
0.608, 0.794, 0.555,
0.614, 0.796, 0.558,
0.620, 0.798, 0.561,
0.626, 0.800, 0.563,
0.631, 0.802, 0.566,
0.637, 0.804, 0.569,
0.643, 0.806, 0.572,
0.648, 0.808, 0.575,
0.654, 0.809, 0.578,
0.660, 0.811, 0.581,
0.665, 0.813, 0.584,
0.671, 0.815, 0.587,
0.676, 0.817, 0.591,
0.682, 0.819, 0.594,
0.687, 0.820, 0.597,
0.693, 0.822, 0.601,
0.699, 0.824, 0.604,
0.704, 0.826, 0.608,
0.709, 0.828, 0.611,
0.715, 0.829, 0.615,
0.720, 0.831, 0.618,
0.726, 0.833, 0.622,
0.731, 0.835, 0.626,
0.736, 0.836, 0.630,
0.742, 0.838, 0.633,
0.747, 0.840, 0.637,
0.752, 0.842, 0.641,
0.757, 0.843, 0.645,
0.762, 0.845, 0.649,
0.768, 0.847, 0.653,
0.773, 0.849, 0.657,
0.778, 0.850, 0.662,
0.783, 0.852, 0.666,
0.788, 0.854, 0.670,
0.793, 0.856, 0.674,
0.798, 0.857, 0.679,
0.802, 0.859, 0.683,
0.807, 0.861, 0.688,
0.812, 0.863, 0.692,
0.817, 0.865, 0.697,
0.821, 0.866, 0.701,
0.826, 0.868, 0.706,
0.831, 0.870, 0.710,
0.835, 0.872, 0.715,
0.840, 0.874, 0.720,
0.844, 0.876, 0.724,
0.849, 0.877, 0.729,
0.853, 0.879, 0.734,
0.857, 0.881, 0.739,
0.862, 0.883, 0.744,
0.866, 0.885, 0.749,
0.870, 0.887, 0.754,
0.874, 0.889, 0.759,
0.878, 0.891, 0.764,
0.882, 0.893, 0.769,
0.886, 0.895, 0.774,
0.890, 0.897, 0.779,
0.894, 0.899, 0.784,
0.898, 0.901, 0.789,
0.901, 0.903, 0.794,
0.905, 0.905, 0.799,
0.909, 0.907, 0.804,
0.912, 0.909, 0.810,
0.916, 0.911, 0.815,
0.919, 0.913, 0.820,
0.922, 0.915, 0.825,
0.926, 0.917, 0.831,
0.929, 0.920, 0.836,
0.932, 0.922, 0.841,
0.935, 0.924, 0.847,
0.938, 0.926, 0.852,
0.942, 0.929, 0.857,
0.944, 0.931, 0.863,
0.947, 0.933, 0.868,
0.950, 0.936, 0.873,
0.953, 0.938, 0.879,
0.956, 0.940, 0.884,
0.958, 0.943, 0.889,
0.961, 0.945, 0.895,
0.964, 0.948, 0.900,
0.966, 0.950, 0.905,
0.968, 0.953, 0.911,
0.971, 0.955, 0.916,
0.973, 0.958, 0.921,
0.975, 0.961, 0.927,
0.977, 0.963, 0.932,
0.980, 0.966, 0.937,
0.982, 0.969, 0.943,
0.984, 0.971, 0.948,
0.985, 0.974, 0.953,
0.987, 0.977, 0.959,
0.989, 0.980, 0.964,
0.991, 0.982, 0.969,
0.993, 0.985, 0.974,
0.994, 0.988, 0.979,
0.996, 0.991, 0.985,
0.997, 0.994, 0.990,
0.999, 0.997, 0.995,
1.000, 1.000, 1.000,
};

View File

@ -66,6 +66,9 @@ private:
static const float m_shrimp[m_size];
static const float m_cubehelix[m_size];
static const float m_cubegamma[m_size];
static const float m_cubehlx2[m_size];
static const float m_icy[m_size];
static const float m_mint[m_size];
};
#endif

View File

@ -12,15 +12,21 @@ set(wdsp_SOURCES
bldr.cpp
bps.cpp
bpsnba.cpp
bqbp.cpp
bqlp.cpp
calculus.cpp
cblock.cpp
cfcomp.cpp
cfir.cpp
compress.cpp
dbqbp.cpp
dbqlp.cpp
delay.cpp
dsphp.cpp
emnr.cpp
emph.cpp
eq.cpp
emphp.cpp
eqp.cpp
fcurve.cpp
fir.cpp
fircore.cpp
@ -32,15 +38,17 @@ set(wdsp_SOURCES
gain.cpp
gen.cpp
icfir.cpp
iir.cpp
# iir.cpp
iqc.cpp
lmath.cpp
meter.cpp
meterlog10.cpp
mpeak.cpp
nbp.cpp
nob.cpp
osctrl.cpp
patchpanel.cpp
phrot.cpp
resample.cpp
resamplef.cpp
rmatch.cpp
@ -50,8 +58,12 @@ set(wdsp_SOURCES
siphon.cpp
slew.cpp
snba.cpp
snotch.cpp
speak.cpp
sphp.cpp
ssql.cpp
TXA.cpp
unit.cpp
varsamp.cpp
wcpAGC.cpp
)
@ -67,6 +79,8 @@ set(wdsp_HEADERS
bldr.hpp
bps.hpp
bpsnba.hpp
bqbp.hpp
bqlp.hpp
bufferprobe.hpp
calculus.hpp
cblock.hpp
@ -74,10 +88,14 @@ set(wdsp_HEADERS
cfir.hpp
comm.hpp
compress.hpp
dbqbp.hpp
dbqlp.hpp
delay.hpp
dsphp.hpp
emnr.hpp
emph.hpp
eq.hpp
emphp.hpp
eqp.hpp
fcurve.hpp
fir.hpp
fircore.hpp
@ -89,15 +107,17 @@ set(wdsp_HEADERS
gain.hpp
gen.hpp
icfir.hpp
iir.hpp
# iir.hpp
iqc.hpp
lmath.hpp
meter.hpp
meterlog10.hpp
mpeak.hpp
nbp.hpp
nob.hpp
osctrl.hpp
patchpanel.hpp
phrot.hpp
resample.hpp
resamplef.hpp
rmatch.hpp
@ -107,8 +127,12 @@ set(wdsp_HEADERS
siphon.hpp
slew.hpp
snba.hpp
snotch.hpp
speak.hpp
sphp.hpp
ssql.hpp
TXA.hpp
unit.hpp
varsamp.hpp
wcpAGC.hpp
)

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,8 @@ warren@wpratt.com
#ifndef wdsp_rxa_h
#define wdsp_rxa_h
#include <array>
#include "comm.hpp"
#include "unit.hpp"
#include "export.h"
@ -37,7 +39,6 @@ namespace WDSP {
class METER;
class SHIFT;
class RESAMPLE;
class GEN;
class BANDPASS;
class BPS;
class NOTCHDB;
@ -96,157 +97,101 @@ public:
};
int mode;
double meter[RXA_METERTYPE_LAST];
std::array<double, RXA_METERTYPE_LAST> meter;
struct
{
METER *p;
} smeter, adcmeter, agcmeter;
struct
{
SHIFT *p;
} shift;
struct
{
RESAMPLE *p;
} rsmpin, rsmpout;
struct
{
GEN *p;
} gen0;
struct
{
BANDPASS *p;
} bp1;
struct
{
BPS *p;
} bps1;
struct
{
NOTCHDB *p;
} ndb;
struct
{
NBP *p;
} nbp0;
struct
{
BPSNBA *p;
} bpsnba;
struct
{
SNBA *p;
} snba;
struct
{
SENDER *p;
} sender;
struct
{
AMSQ *p;
} amsq;
struct
{
AMD *p;
} amd;
struct
{
FMD *p;
} fmd;
struct
{
FMSQ *p;
} fmsq;
struct
{
EQP *p;
} eqp;
struct
{
ANF *p;
} anf;
struct
{
ANR *p;
} anr;
struct
{
EMNR *p;
} emnr;
struct
{
WCPAGC *p;
} agc;
struct
{
SPEAK *p;
} speak;
struct
{
MPEAK *p;
} mpeak;
struct
{
PANEL *p;
} panel;
struct
{
SIPHON *p;
} sip1;
struct
{
CBL *p;
} cbl;
struct
{
SSQL *p;
} ssql;
struct
{
ANB *p;
} anb;
struct
{
NOB *p;
} nob;
ANB *anb;
NOB *nob;
SHIFT *shift;
RESAMPLE *rsmpin;
METER *adcmeter;
NOTCHDB *ndb;
NBP *nbp0;
BPSNBA *bpsnba;
SENDER *sender;
METER *smeter;
AMSQ *amsq;
AMD *amd;
FMD *fmd;
FMSQ *fmsq;
SNBA *snba;
EQP *eqp;
ANF *anf;
ANR *anr;
EMNR *emnr;
WCPAGC *agc;
METER *agcmeter;
BANDPASS *bp1;
SIPHON *sip1;
CBL *cbl;
SPEAK *speak;
MPEAK *mpeak;
SSQL *ssql;
PANEL *panel;
RESAMPLE *rsmpout;
static RXA* create_rxa (
RXA(
int in_rate, // input samplerate
int out_rate, // output samplerate
int dsp_rate, // sample rate for mainstream dsp processing
int dsp_size // number complex samples processed per buffer in mainstream dsp processing
);
static void destroy_rxa (RXA *rxa);
static void flush_rxa (RXA *rxa);
static void xrxa (RXA *rxa);
int get_insize() const { return dsp_insize; }
int get_outsize() const { return dsp_outsize; }
float *get_inbuff() { return inbuff; }
float *get_outbuff() { return outbuff; }
RXA(const RXA&) = delete;
RXA& operator=(const RXA& other) = delete;
virtual ~RXA();
void flush();
void execute();
void setInputSamplerate(int _in_rate);
void setOutputSamplerate(int _out_rate);
void setDSPSamplerate(int _dsp_rate);
void setDSPBuffsize(int _dsp_size);
int get_insize() const { return Unit::dsp_insize; }
int get_outsize() const { return Unit::dsp_outsize; }
float *get_inbuff() { return Unit::inbuff; }
float *get_outbuff() { return Unit::outbuff; }
void setSpectrumProbe(BufferProbe *_spectrumProbe);
static void setInputSamplerate (RXA *rxa, int in_rate);
static void setOutputSamplerate (RXA *rxa, int out_rate);
static void setDSPSamplerate (RXA *rxa, int dsp_rate);
static void setDSPBuffsize (RXA *rxa, int dsp_size);
// RXA Properties
static void SetMode (RXA& rxa, int mode);
static void ResCheck (RXA& rxa);
static void bp1Check (RXA& rxa, int amd_run, int snba_run, int emnr_run, int anf_run, int anr_run);
static void bp1Set (RXA& rxa);
static void bpsnbaCheck (RXA& rxa, int mode, int notch_run);
static void bpsnbaSet (RXA& rxa);
void setMode (int mode);
void resCheck ();
void bp1Check (int amd_run, int snba_run, int emnr_run, int anf_run, int anr_run);
void bp1Set ();
void bpsnbaCheck (int mode, int notch_run);
void bpsnbaSet ();
// NOTCHDB, NBP, SNBA
void updateNBPFiltersLightWeight();
void updateNBPFilters();
int nbpAddNotch(int notch, double fcenter, double fwidth, int active);
int nbpGetNotch(int notch, double* fcenter, double* fwidth, int* active) const;
int nbpDeleteNotch(int notch);
int nbpEditNotch(int notch, double fcenter, double fwidth, int active);
void nbpGetNumNotches(int* nnotches) const;
void nbpSetTuneFrequency(double tunefreq);
void nbpSetShiftFrequency(double shift);
void nbpSetNotchesRun(int run);
void nbpSetWindow(int wintype);
void nbpSetAutoIncrease(int autoincr);
// AMD
void setAMDRun(int run);
// SNBA
void setSNBARun(int run);
// ANF
void setANFRun(int run);
void setANFPosition(int position);
// ANR
void setANRRun(int run);
void setANRPosition(int position);
// EMNR
void setEMNRRun(int run);
void setEMNRPosition(int position);
// WCPAGC
void setAGCThresh(double thresh, double size, double rate);
void getAGCThresh(double *thresh, double size, double rate) const;
// Collectives
static void SetPassband (RXA& rxa, float f_low, float f_high);
static void SetNC (RXA& rxa, int nc);
static void SetMP (RXA& rxa, int mp);
private:
float* inbuff;
float* midbuff;
float* outbuff;
void setPassband(float f_low, float f_high);
void setNC(int nc);
void setMP(int mp);
};
} // namespace WDSP

File diff suppressed because it is too large Load Diff

View File

@ -36,8 +36,7 @@ warren@wpratt.com
#include "resample.hpp"
#include "patchpanel.hpp"
#include "amsq.hpp"
#include "eq.hpp"
#include "iir.hpp"
#include "eqp.hpp"
#include "cfcomp.hpp"
#include "compress.hpp"
#include "bandpass.hpp"
@ -127,127 +126,96 @@ public:
double meter[TXA_METERTYPE_LAST];
long upslew;
struct
{
METER *p;
} micmeter, eqmeter, lvlrmeter, cfcmeter, compmeter, alcmeter, outmeter;
struct
{
RESAMPLE *p;
} rsmpin, rsmpout;
struct
{
PANEL *p;
} panel;
struct
{
AMSQ *p;
} amsq;
struct
{
EQP *p;
} eqp;
struct
{
PHROT *p;
} phrot;
struct
{
CFCOMP *p;
} cfcomp;
struct
{
COMPRESSOR *p;
} compressor;
struct
{
BANDPASS *p;
} bp0, bp1, bp2;
struct
{
BPS *p;
} bps0, bps1, bps2;
struct
{
OSCTRL *p;
} osctrl;
struct
{
WCPAGC *p;
} leveler, alc;
struct
{
AMMOD *p;
} ammod;
struct
{
EMPHP *p;
} preemph;
struct
{
FMMOD *p;
} fmmod;
struct
{
SIPHON *p;
} sip1;
struct
{
GEN *p;
} gen0, gen1;
struct
{
USLEW *p;
} uslew;
// struct
// {
// CALCC *p;
// CRITICAL_SECTION cs_update;
// } calcc;
METER *micmeter;
METER *eqmeter;
METER *lvlrmeter;
METER *cfcmeter;
METER *compmeter;
METER *alcmeter;
METER *outmeter;
RESAMPLE *rsmpin;
RESAMPLE *rsmpout;
PANEL *panel;
AMSQ *amsq;
EQP *eqp;
PHROT *phrot;
CFCOMP *cfcomp;
COMPRESSOR *compressor;
BANDPASS *bp0;
BANDPASS *bp1;
BANDPASS *bp2;
BPS *bps0;
BPS *bps1;
BPS *bps2;
OSCTRL *osctrl;
WCPAGC *leveler;
WCPAGC *alc;
AMMOD *ammod;
EMPHP *preemph;
FMMOD *fmmod;
SIPHON *sip1;
GEN *gen0;
GEN *gen1;
USLEW *uslew;
struct
{
IQC *p0, *p1;
// p0 for dsp-synchronized reference, p1 for other
} iqc;
struct
{
CFIR *p;
} cfir;
CFIR *cfir;
static TXA* create_txa (
TXA(
int in_rate, // input samplerate
int out_rate, // output samplerate
int dsp_rate, // sample rate for mainstream dsp processing
int dsp_size // number complex samples processed per buffer in mainstream dsp processing
);
static void destroy_txa (TXA *txa);
static void flush_txa (TXA *txa);
static void xtxa (TXA *txa);
int get_insize() const { return dsp_insize; }
int get_outsize() const { return dsp_outsize; }
float *get_inbuff() { return inbuff; }
float *get_outbuff() { return outbuff; }
static void setInputSamplerate (TXA *txa, int in_rate);
static void setOutputSamplerate (TXA *txa, int out_rate);
static void setDSPSamplerate (TXA *txa, int dsp_rate);
static void setDSPBuffsize (TXA *txa, int dsp_size);
TXA(const TXA&) = delete;
TXA& operator=(const TXA& other) = delete;
virtual ~TXA();
void flush();
void execute();
void setInputSamplerate(int _in_rate);
void setOutputSamplerate(int _out_rate);
void setDSPSamplerate(int _dsp_rate);
void setDSPBuffsize(int _dsp_size);
int get_insize() const { return Unit::dsp_insize; }
int get_outsize() const { return Unit::dsp_outsize; }
float *get_inbuff() { return Unit::inbuff; }
float *get_outbuff() { return Unit::outbuff; }
// TXA Properties
static void SetMode (TXA& txa, int mode);
static void SetBandpassFreqs (TXA& txa, float f_low, float f_high);
void setMode(int mode);
void setBandpassFreqs(float f_low, float f_high);
void setBandpassNC(int nc);
void setBandpassMP(int mp);
// BPS
static void SetBPSRun (TXA& txa, int run);
static void SetBPSFreqs (TXA& txa, double low, double high);
static void SetBPSWindow (TXA& txa, int wintype);
// COMPRESSOR
static void SetCompressorRun (TXA& txa, int run);
// OSCTRL
static void SetosctrlRun (TXA& txa, int run);
// IQC
static void GetiqcValues (TXA& txa, std::vector<double>& cm, std::vector<double>& cc, std::vector<double>& cs);
static void SetiqcValues (TXA& txa, const std::vector<double>& cm, const std::vector<double>& cc, const std::vector<double>& cs);
static void SetiqcSwap (TXA& txa, const std::vector<double>& cm, const std::vector<double>& cc, const std::vector<double>& cs);
static void SetiqcStart (TXA& txa, const std::vector<double>& cm, const std::vector<double>& cc, const std::vector<double>& cs);
static void SetiqcEnd (TXA& txa);
static void GetiqcDogCount (TXA& txa, int* count);
static void SetiqcDogCount (TXA& txa, int count);
// Collectives
static void SetNC (TXA& txa, int nc);
static void SetMP (TXA& txa, int mp);
static void SetFMAFFilter (TXA& txa, float low, float high);
static void SetupBPFilters (TXA& txa);
static int UslewCheck (TXA& txa);
void setNC(int nc);
void setMP(int mp);
void setFMAFFilter(float low, float high);
void setupBPFilters();
int uslewCheck();
private:
static void ResCheck (TXA& txa);
float* inbuff;
float* midbuff;
float* outbuff;
void resCheck();
};
} // namespace WDSP

View File

@ -26,6 +26,7 @@ warren@wpratt.com
*/
#include <cmath>
#include <array>
#include "comm.hpp"
#include "amd.hpp"
@ -33,130 +34,125 @@ warren@wpratt.com
#include "emnr.hpp"
#include "anr.hpp"
#include "snba.hpp"
#include "RXA.hpp"
namespace WDSP {
AMD* AMD::create_amd
AMD::AMD
(
int run,
int buff_size,
float *in_buff,
float *out_buff,
int mode,
int levelfade,
int sbmode,
int sample_rate,
double fmin,
double fmax,
double zeta,
double omegaN,
double tauR,
double tauI
)
int _run,
int _buff_size,
float *_in_buff,
float *_out_buff,
int _mode,
int _levelfade,
int _sbmode,
int _sample_rate,
double _fmin,
double _fmax,
double _zeta,
double _omegaN,
double _tauR,
double _tauI
) :
run(_run),
buff_size(_buff_size),
in_buff(_in_buff),
out_buff(_out_buff),
mode(_mode),
sample_rate((double) _sample_rate),
fmin(_fmin),
fmax(_fmax),
zeta(_zeta),
omegaN(_omegaN),
tauR(_tauR),
tauI(_tauI),
sbmode(_sbmode),
levelfade(_levelfade)
{
AMD *a = new AMD();
a->run = run;
a->buff_size = buff_size;
a->in_buff = in_buff;
a->out_buff = out_buff;
a->mode = mode;
a->levelfade = levelfade;
a->sbmode = sbmode;
a->sample_rate = (float)sample_rate;
a->fmin = fmin;
a->fmax = fmax;
a->zeta = zeta;
a->omegaN = omegaN;
a->tauR = tauR;
a->tauI = tauI;
init_amd(a);
return a;
init();
}
void AMD::destroy_amd(AMD *a)
{
delete a;
}
void AMD::init_amd(AMD *a)
void AMD::init()
{
//pll
a->omega_min = 2 * M_PI * a->fmin / a->sample_rate;
a->omega_max = 2 * M_PI * a->fmax / a->sample_rate;
a->g1 = 1.0 - std::exp(-2.0 * a->omegaN * a->zeta / a->sample_rate);
a->g2 = -a->g1 + 2.0 * (1 - exp(-a->omegaN * a->zeta / a->sample_rate) * cos(a->omegaN / a->sample_rate * sqrt(1.0 - a->zeta * a->zeta)));
a->phs = 0.0;
a->fil_out = 0.0;
a->omega = 0.0;
omega_min = 2 * M_PI * fmin / sample_rate;
omega_max = 2 * M_PI * fmax / sample_rate;
g1 = 1.0 - std::exp(-2.0 * omegaN * zeta / sample_rate);
g2 = -g1 + 2.0 * (1 - exp(-omegaN * zeta / sample_rate) * cos(omegaN / sample_rate * sqrt(1.0 - zeta * zeta)));
phs = 0.0;
fil_out = 0.0;
omega = 0.0;
//fade leveler
a->dc = 0.0;
a->dc_insert = 0.0;
a->mtauR = exp(-1.0 / (a->sample_rate * a->tauR));
a->onem_mtauR = 1.0 - a->mtauR;
a->mtauI = exp(-1.0 / (a->sample_rate * a->tauI));
a->onem_mtauI = 1.0 - a->mtauI;
dc = 0.0;
dc_insert = 0.0;
mtauR = exp(-1.0 / (sample_rate * tauR));
onem_mtauR = 1.0 - mtauR;
mtauI = exp(-1.0 / (sample_rate * tauI));
onem_mtauI = 1.0 - mtauI;
//sideband separation
a->c0[0] = -0.328201924180698;
a->c0[1] = -0.744171491539427;
a->c0[2] = -0.923022915444215;
a->c0[3] = -0.978490468768238;
a->c0[4] = -0.994128272402075;
a->c0[5] = -0.998458978159551;
a->c0[6] = -0.999790306259206;
c0[0] = -0.328201924180698;
c0[1] = -0.744171491539427;
c0[2] = -0.923022915444215;
c0[3] = -0.978490468768238;
c0[4] = -0.994128272402075;
c0[5] = -0.998458978159551;
c0[6] = -0.999790306259206;
a->c1[0] = -0.0991227952747244;
a->c1[1] = -0.565619728761389;
a->c1[2] = -0.857467122550052;
a->c1[3] = -0.959123933111275;
a->c1[4] = -0.988739372718090;
a->c1[5] = -0.996959189310611;
a->c1[6] = -0.999282492800792;
c1[0] = -0.0991227952747244;
c1[1] = -0.565619728761389;
c1[2] = -0.857467122550052;
c1[3] = -0.959123933111275;
c1[4] = -0.988739372718090;
c1[5] = -0.996959189310611;
c1[6] = -0.999282492800792;
}
void AMD::flush_amd (AMD *a)
void AMD::flush()
{
a->dc = 0.0;
a->dc_insert = 0.0;
dc = 0.0;
dc_insert = 0.0;
}
void AMD::xamd (AMD *a)
void AMD::execute()
{
int i;
double audio;
double vco[2];
double corr[2];
std::array<double, 2> vco;
std::array<double, 2> corr;
double det;
double del_out;
double ai, bi, aq, bq;
double ai_ps, bi_ps, aq_ps, bq_ps;
int j, k;
double ai;
double bi;
double aq;
double bq;
double ai_ps;
double bi_ps;
double aq_ps;
double bq_ps;
if (a->run)
if (run)
{
switch (a->mode)
switch (mode)
{
case 0: //AM Demodulator
{
for (i = 0; i < a->buff_size; i++)
for (int i = 0; i < buff_size; i++)
{
double xr = a->in_buff[2 * i + 0];
double xi = a->in_buff[2 * i + 1];
double xr = in_buff[2 * i + 0];
double xi = in_buff[2 * i + 1];
audio = sqrt(xr*xr + xi*xi);
if (a->levelfade)
if (levelfade)
{
a->dc = a->mtauR * a->dc + a->onem_mtauR * audio;
a->dc_insert = a->mtauI * a->dc_insert + a->onem_mtauI * audio;
audio += a->dc_insert - a->dc;
dc = mtauR * dc + onem_mtauR * audio;
dc_insert = mtauI * dc_insert + onem_mtauI * audio;
audio += dc_insert - dc;
}
a->out_buff[2 * i + 0] = audio;
a->out_buff[2 * i + 1] = audio;
out_buff[2 * i + 0] = (float) audio;
out_buff[2 * i + 1] = (float) audio;
}
break;
@ -164,52 +160,52 @@ void AMD::xamd (AMD *a)
case 1: //Synchronous AM Demodulator with Sideband Separation
{
for (i = 0; i < a->buff_size; i++)
for (int i = 0; i < buff_size; i++)
{
vco[0] = cos(a->phs);
vco[1] = sin(a->phs);
vco[0] = cos(phs);
vco[1] = sin(phs);
ai = a->in_buff[2 * i + 0] * vco[0];
bi = a->in_buff[2 * i + 0] * vco[1];
aq = a->in_buff[2 * i + 1] * vco[0];
bq = a->in_buff[2 * i + 1] * vco[1];
ai = in_buff[2 * i + 0] * vco[0];
bi = in_buff[2 * i + 0] * vco[1];
aq = in_buff[2 * i + 1] * vco[0];
bq = in_buff[2 * i + 1] * vco[1];
if (a->sbmode != 0)
if (sbmode != 0)
{
a->a[0] = a->dsI;
a->b[0] = bi;
a->c[0] = a->dsQ;
a->d[0] = aq;
a->dsI = ai;
a->dsQ = bq;
a[0] = dsI;
b[0] = bi;
c[0] = dsQ;
d[0] = aq;
dsI = ai;
dsQ = bq;
for (j = 0; j < STAGES; j++)
for (int j = 0; j < STAGES; j++)
{
k = 3 * j;
a->a[k + 3] = a->c0[j] * (a->a[k] - a->a[k + 5]) + a->a[k + 2];
a->b[k + 3] = a->c1[j] * (a->b[k] - a->b[k + 5]) + a->b[k + 2];
a->c[k + 3] = a->c0[j] * (a->c[k] - a->c[k + 5]) + a->c[k + 2];
a->d[k + 3] = a->c1[j] * (a->d[k] - a->d[k + 5]) + a->d[k + 2];
int k = 3 * j;
a[k + 3] = c0[j] * (a[k] - a[k + 5]) + a[k + 2];
b[k + 3] = c1[j] * (b[k] - b[k + 5]) + b[k + 2];
c[k + 3] = c0[j] * (c[k] - c[k + 5]) + c[k + 2];
d[k + 3] = c1[j] * (d[k] - d[k + 5]) + d[k + 2];
}
ai_ps = a->a[OUT_IDX];
bi_ps = a->b[OUT_IDX];
bq_ps = a->c[OUT_IDX];
aq_ps = a->d[OUT_IDX];
ai_ps = a[OUT_IDX];
bi_ps = b[OUT_IDX];
bq_ps = c[OUT_IDX];
aq_ps = d[OUT_IDX];
for (j = OUT_IDX + 2; j > 0; j--)
for (int j = OUT_IDX + 2; j > 0; j--)
{
a->a[j] = a->a[j - 1];
a->b[j] = a->b[j - 1];
a->c[j] = a->c[j - 1];
a->d[j] = a->d[j - 1];
a[j] = a[j - 1];
b[j] = b[j - 1];
c[j] = c[j - 1];
d[j] = d[j - 1];
}
}
corr[0] = +ai + bq;
corr[1] = -bi + aq;
switch(a->sbmode)
switch(sbmode)
{
case 0: //both sidebands
{
@ -226,102 +222,86 @@ void AMD::xamd (AMD *a)
audio = (ai_ps + bi_ps) - (aq_ps - bq_ps);
break;
}
default:
break;
}
if (a->levelfade)
if (levelfade)
{
a->dc = a->mtauR * a->dc + a->onem_mtauR * audio;
a->dc_insert = a->mtauI * a->dc_insert + a->onem_mtauI * corr[0];
audio += a->dc_insert - a->dc;
dc = mtauR * dc + onem_mtauR * audio;
dc_insert = mtauI * dc_insert + onem_mtauI * corr[0];
audio += dc_insert - dc;
}
a->out_buff[2 * i + 0] = audio;
a->out_buff[2 * i + 1] = audio;
out_buff[2 * i + 0] = (float) audio;
out_buff[2 * i + 1] = (float) audio;
if ((corr[0] == 0.0) && (corr[1] == 0.0))
corr[0] = 1.0;
det = atan2(corr[1], corr[0]);
del_out = a->fil_out;
a->omega += a->g2 * det;
del_out = fil_out;
omega += g2 * det;
if (a->omega < a->omega_min)
a->omega = a->omega_min;
if (omega < omega_min)
omega = omega_min;
if (a->omega > a->omega_max)
a->omega = a->omega_max;
if (omega > omega_max)
omega = omega_max;
a->fil_out = a->g1 * det + a->omega;
a->phs += del_out;
fil_out = g1 * det + omega;
phs += del_out;
while (a->phs >= 2 * M_PI)
a->phs -= 2 * M_PI;
while (phs >= 2 * M_PI)
phs -= 2 * M_PI;
while (a->phs < 0.0)
a->phs += 2 * M_PI;
while (phs < 0.0)
phs += 2 * M_PI;
}
break;
}
default:
break;
}
}
else if (a->in_buff != a->out_buff)
else if (in_buff != out_buff)
{
std::copy (a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff);
std::copy (in_buff, in_buff + buff_size * 2, out_buff);
}
}
void AMD::setBuffers_amd (AMD *a, float* in, float* out)
void AMD::setBuffers(float* in, float* out)
{
a->in_buff = in;
a->out_buff = out;
in_buff = in;
out_buff = out;
}
void AMD::setSamplerate_amd (AMD *a, int rate)
void AMD::setSamplerate(int rate)
{
a->sample_rate = rate;
init_amd(a);
sample_rate = rate;
init();
}
void AMD::setSize_amd (AMD *a, int size)
void AMD::setSize(int size)
{
a->buff_size = size;
buff_size = size;
}
/********************************************************************************************************
* *
* RXA Properties *
* Public Properties *
* *
********************************************************************************************************/
void AMD::SetAMDRun(RXA& rxa, int run)
void AMD::setSBMode(int _sbmode)
{
AMD *a = rxa.amd.p;
if (a->run != run)
{
RXA::bp1Check (
rxa,
run,
rxa.snba.p->run,
rxa.emnr.p->run,
rxa.anf.p->run,
rxa.anr.p->run
);
a->run = run;
RXA::bp1Set (rxa);
}
sbmode = _sbmode;
}
void AMD::SetAMDSBMode(RXA& rxa, int sbmode)
void AMD::setFadeLevel(int _levelfade)
{
rxa.amd.p->sbmode = sbmode;
}
void AMD::SetAMDFadeLevel(RXA& rxa, int levelfade)
{
rxa.amd.p->levelfade = levelfade;
levelfade = _levelfade;
}
} // namesoace WDSP

View File

@ -28,21 +28,12 @@ warren@wpratt.com
#ifndef wdsp_amd_hpp
#define wdsp_amd_hpp
// ff defines for sbdemod
#ifndef STAGES
#define STAGES 7
#endif
#ifndef OUT_IDX
#define OUT_IDX (3 * STAGES)
#endif
#include <array>
#include "export.h"
namespace WDSP {
class RXA;
class WDSP_API AMD {
public:
int run;
@ -61,26 +52,29 @@ public:
double phs; // pll - phase accumulator
double omega; // pll - locked pll frequency
double fil_out; // pll - filter output
double g1, g2; // pll - filter gain parameters
double g1; // pll - filter gain parameters
double g2; // pll - filter gain parameters
double tauR; // carrier removal time constant
double tauI; // carrier insertion time constant
double mtauR; // carrier removal multiplier
double onem_mtauR; // 1.0 - carrier_removal_multiplier
double mtauI; // carrier insertion multiplier
double onem_mtauI; // 1.0 - carrier_insertion_multiplier
double a[3 * STAGES + 3]; // Filter a variables
double b[3 * STAGES + 3]; // Filter b variables
double c[3 * STAGES + 3]; // Filter c variables
double d[3 * STAGES + 3]; // Filter d variables
double c0[STAGES]; // Filter coefficients - path 0
double c1[STAGES]; // Filter coefficients - path 1
static const int STAGES = 7;
static const int OUT_IDX = 3 * STAGES;
std::array<double, 3*STAGES + 3> a; // Filter a variables
std::array<double, 3*STAGES + 3> b; // Filter b variables
std::array<double, 3*STAGES + 3> c; // Filter c variables
std::array<double, 3*STAGES + 3> d; // Filter d variables
std::array<double, STAGES> c0; // Filter coefficients - path 0
std::array<double, STAGES> c1; // Filter coefficients - path 1
double dsI; // delayed sample, I path
double dsQ; // delayed sample, Q path
double dc_insert; // dc component to insert in output
int sbmode; // sideband mode
int levelfade; // Fade Leveler switch
static AMD* create_amd
AMD
(
int run,
int buff_size,
@ -97,18 +91,19 @@ public:
double tauR,
double tauI
);
AMD(const AMD&) = delete;
AMD& operator=(const AMD& other) = delete;
~AMD() = default;
static void init_amd (AMD *a);
static void destroy_amd (AMD *a);
static void flush_amd (AMD *a);
static void xamd (AMD *a);
static void setBuffers_amd (AMD *a, float* in, float* out);
static void setSamplerate_amd (AMD *a, int rate);
static void setSize_amd (AMD *a, int size);
// RXA Properties
static void SetAMDRun(RXA& rxa, int run);
static void SetAMDSBMode(RXA& rxa, int sbmode);
static void SetAMDFadeLevel(RXA& rxa, int levelfade);
void init();
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// Public Properties
void setSBMode(int sbmode);
void setFadeLevel(int levelfade);
};
} // namespace WDSP

View File

@ -33,72 +33,74 @@ warren@wpratt.com
namespace WDSP {
AMMOD* AMMOD::create_ammod (int run, int mode, int size, float* in, float* out, float c_level)
AMMOD::AMMOD(
int _run,
int _mode,
int _size,
float* _in,
float* _out,
double _c_level
)
{
AMMOD *a = new AMMOD;
a->run = run;
a->mode = mode;
a->size = size;
a->in = in;
a->out = out;
a->c_level = c_level;
a->a_level = 1.0 - a->c_level;
a->mult = 1.0 / sqrt (2.0);
return a;
run = _run;
mode = _mode;
size = _size;
in = _in;
out = _out;
c_level = _c_level;
a_level = 1.0 - c_level;
mult = 1.0 / sqrt (2.0);
}
void AMMOD::destroy_ammod(AMMOD *a)
void AMMOD::flush()
{
delete a;
// Nothing to flush
}
void AMMOD::flush_ammod(AMMOD *)
void AMMOD::execute()
{
}
void AMMOD::xammod(AMMOD *a)
{
if (a->run)
if (run)
{
int i;
switch (a->mode)
switch (mode)
{
case 0: // AM
for (i = 0; i < a->size; i++)
a->out[2 * i + 0] = a->out[2 * i + 1] = a->mult * (a->c_level + a->a_level * a->in[2 * i + 0]);
for (i = 0; i < size; i++)
out[2 * i + 0] = out[2 * i + 1] = (float) (mult * (c_level + a_level * in[2 * i + 0]));
break;
case 1: // DSB
for (i = 0; i < a->size; i++)
a->out[2 * i + 0] = a->out[2 * i + 1] = a->mult * a->in[2 * i + 0];
for (i = 0; i < size; i++)
out[2 * i + 0] = out[2 * i + 1] = (float) (mult * in[2 * i + 0]);
break;
case 2: // SSB w/Carrier
for (i = 0; i < a->size; i++)
for (i = 0; i < size; i++)
{
a->out[2 * i + 0] = a->mult * a->c_level + a->a_level * a->in[2 * i + 0];
a->out[2 * i + 1] = a->mult * a->c_level + a->a_level * a->in[2 * i + 1];
out[2 * i + 0] = (float) (mult * c_level + a_level * in[2 * i + 0]);
out[2 * i + 1] = (float) (mult * c_level + a_level * in[2 * i + 1]);
}
break;
default:
break;
}
}
else if (a->in != a->out)
std::copy( a->in, a->in + a->size * 2, a->out);
else if (in != out)
std::copy( in, in + size * 2, out);
}
void AMMOD::setBuffers_ammod(AMMOD *a, float* in, float* out)
void AMMOD::setBuffers(float* _in, float* _out)
{
a->in = in;
a->out = out;
in = _in;
out = _out;
}
void AMMOD::setSamplerate_ammod(AMMOD *, int)
void AMMOD::setSamplerate(int)
{
// Nothing to do
}
void AMMOD::setSize_ammod(AMMOD *a, int size)
void AMMOD::setSize(int _size)
{
a->size = size;
size = _size;
}
/********************************************************************************************************
@ -107,10 +109,10 @@ void AMMOD::setSize_ammod(AMMOD *a, int size)
* *
********************************************************************************************************/
void AMMOD::SetAMCarrierLevel (TXA& txa, float c_level)
void AMMOD::setAMCarrierLevel(double _c_level)
{
txa.ammod.p->c_level = c_level;
txa.ammod.p->a_level = 1.0 - c_level;
c_level = _c_level;
a_level = 1.0 - _c_level;
}
} // namespace WDSP

View File

@ -42,19 +42,29 @@ public:
int size;
float* in;
float* out;
float c_level;
float a_level;
float mult;
double c_level;
double a_level;
double mult;
static AMMOD* create_ammod(int run, int mode, int size, float* in, float* out, float c_level);
static void destroy_ammod (AMMOD *a);
static void flush_ammod (AMMOD *a);
static void xammod (AMMOD *a);
static void setBuffers_ammod (AMMOD *a, float* in, float* out);
static void setSamplerate_ammod (AMMOD *a, int rate);
static void setSize_ammod (AMMOD *a, int size);
AMMOD(
int run,
int mode,
int size,
float* in,
float* out,
double c_level
);
AMMOD(const AMMOD&) = delete;
AMMOD& operator=(const AMMOD& other) = delete;
~AMMOD() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// TXA Properties
static void SetAMCarrierLevel (TXA& txa, float c_level);
void setAMCarrierLevel(double c_level);
};
} // namespace WDSP

View File

@ -25,226 +25,202 @@ warren@wpratt.com
*/
#include <cstdio>
#include "comm.hpp"
#include "amsq.hpp"
#include "RXA.hpp"
#include "TXA.hpp"
namespace WDSP {
void AMSQ::compute_slews(AMSQ *a)
void AMSQ::compute_slews()
{
int i;
double delta, theta;
delta = PI / (double)a->ntup;
double delta;
double theta;
delta = PI / (double)ntup;
theta = 0.0;
for (i = 0; i <= a->ntup; i++)
for (int i = 0; i <= ntup; i++)
{
a->cup[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 - cos (theta));
cup[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 - cos (theta));
theta += delta;
}
delta = PI / (double)a->ntdown;
delta = PI / (double)ntdown;
theta = 0.0;
for (i = 0; i <= a->ntdown; i++)
for (int i = 0; i <= ntdown; i++)
{
a->cdown[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 + cos (theta));
cdown[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 + cos (theta));
theta += delta;
}
}
void AMSQ::calc_amsq(AMSQ *a)
void AMSQ::calc()
{
// signal averaging
a->trigsig = new float[a->size * 2];
a->avm = exp(-1.0 / (a->rate * a->avtau));
a->onem_avm = 1.0 - a->avm;
a->avsig = 0.0;
trigsig.resize(size * 2);
avm = exp(-1.0 / (rate * avtau));
onem_avm = 1.0 - avm;
avsig = 0.0;
// level change
a->ntup = (int)(a->tup * a->rate);
a->ntdown = (int)(a->tdown * a->rate);
a->cup = new double[(a->ntup + 1) * 2]; // (float *)malloc0((a->ntup + 1) * sizeof(float));
a->cdown = new double[(a->ntdown + 1) * 2]; // (float *)malloc0((a->ntdown + 1) * sizeof(float));
compute_slews(a);
ntup = (int)(tup * rate);
ntdown = (int)(tdown * rate);
cup.resize((ntup + 1) * 2);
cdown.resize((ntdown + 1) * 2);
compute_slews();
// control
a->state = 0;
state = AMSQState::MUTED;
}
void AMSQ::decalc_amsq (AMSQ *a)
AMSQ::AMSQ (
int _run,
int _size,
float* _in,
float* _out,
float* _trigger,
int _rate,
double _avtau,
double _tup,
double _tdown,
double _tail_thresh,
double _unmute_thresh,
double _min_tail,
double _max_tail,
double _muted_gain
) :
run(_run),
size(_size),
in(_in),
out(_out),
trigger(_trigger),
rate((double) _rate),
avtau(_avtau),
tup(_tup),
tdown(_tdown),
tail_thresh(_tail_thresh),
unmute_thresh(_unmute_thresh),
min_tail(_min_tail),
max_tail(_max_tail),
muted_gain(_muted_gain)
{
delete[] a->cdown;
delete[] a->cup;
delete[] a->trigsig;
calc();
}
AMSQ* AMSQ::create_amsq (
int run,
int size,
float* in,
float* out,
float* trigger,
int rate,
double avtau,
double tup,
double tdown,
double tail_thresh,
double unmute_thresh,
double min_tail,
double max_tail,
double muted_gain
)
void AMSQ::flush()
{
AMSQ *a = new AMSQ;
a->run = run;
a->size = size;
a->in = in;
a->out = out;
a->rate = (float)rate;
a->muted_gain = muted_gain;
a->trigger = trigger;
a->avtau = avtau;
a->tup = tup;
a->tdown = tdown;
a->tail_thresh = tail_thresh;
a->unmute_thresh = unmute_thresh;
a->min_tail = min_tail;
a->max_tail = max_tail;
calc_amsq (a);
return a;
std::fill(trigsig.begin(), trigsig.end(), 0);
avsig = 0.0;
state = AMSQState::MUTED;
}
void AMSQ::destroy_amsq (AMSQ *a)
void AMSQ::execute()
{
decalc_amsq (a);
delete a;
}
void AMSQ::flush_amsq (AMSQ*a)
{
std::fill(a->trigsig, a->trigsig + a->size * 2, 0);
a->avsig = 0.0;
a->state = 0;
}
enum _amsqstate
{
MUTED,
INCREASE,
UNMUTED,
TAIL,
DECREASE
};
void AMSQ::xamsq (AMSQ *a)
{
if (a->run)
if (run)
{
int i;
double sig, siglimit;
double sig;
double siglimit;
for (i = 0; i < a->size; i++)
for (int i = 0; i < size; i++)
{
sig = sqrt (a->trigsig[2 * i + 0] * a->trigsig[2 * i + 0] + a->trigsig[2 * i + 1] * a->trigsig[2 * i + 1]);
a->avsig = a->avm * a->avsig + a->onem_avm * sig;
double trigr = trigsig[2 * i + 0];
double trigi = trigsig[2 * i + 1];
sig = sqrt (trigr*trigr + trigi*trigi);
avsig = avm * avsig + onem_avm * sig;
switch (a->state)
switch (state)
{
case MUTED:
if (a->avsig > a->unmute_thresh)
case AMSQState::MUTED:
if (avsig > unmute_thresh)
{
a->state = INCREASE;
a->count = a->ntup;
state = AMSQState::INCREASE;
count = ntup;
}
a->out[2 * i + 0] = a->muted_gain * a->in[2 * i + 0];
a->out[2 * i + 1] = a->muted_gain * a->in[2 * i + 1];
out[2 * i + 0] = (float) (muted_gain * in[2 * i + 0]);
out[2 * i + 1] = (float) (muted_gain * in[2 * i + 1]);
break;
case INCREASE:
a->out[2 * i + 0] = a->in[2 * i + 0] * a->cup[a->ntup - a->count];
a->out[2 * i + 1] = a->in[2 * i + 1] * a->cup[a->ntup - a->count];
case AMSQState::INCREASE:
out[2 * i + 0] = (float) (in[2 * i + 0] * cup[ntup - count]);
out[2 * i + 1] = (float) (in[2 * i + 1] * cup[ntup - count]);
if (a->count-- == 0)
a->state = UNMUTED;
if (count-- == 0)
state = AMSQState::UNMUTED;
break;
case UNMUTED:
if (a->avsig < a->tail_thresh)
case AMSQState::UNMUTED:
if (avsig < tail_thresh)
{
a->state = TAIL;
state = AMSQState::TAIL;
if ((siglimit = a->avsig) > 1.0)
if ((siglimit = avsig) > 1.0)
siglimit = 1.0;
a->count = (int)((a->min_tail + (a->max_tail - a->min_tail) * (1.0 - siglimit)) * a->rate);
count = (int)((min_tail + (max_tail - min_tail) * (1.0 - siglimit)) * rate);
}
a->out[2 * i + 0] = a->in[2 * i + 0];
a->out[2 * i + 1] = a->in[2 * i + 1];
out[2 * i + 0] = in[2 * i + 0];
out[2 * i + 1] = in[2 * i + 1];
break;
case TAIL:
a->out[2 * i + 0] = a->in[2 * i + 0];
a->out[2 * i + 1] = a->in[2 * i + 1];
case AMSQState::TAIL:
out[2 * i + 0] = in[2 * i + 0];
out[2 * i + 1] = in[2 * i + 1];
if (a->avsig > a->unmute_thresh)
if (avsig > unmute_thresh)
{
a->state = UNMUTED;
state = AMSQState::UNMUTED;
}
else if (a->count-- == 0)
else if (count-- == 0)
{
a->state = DECREASE;
a->count = a->ntdown;
state = AMSQState::DECREASE;
count = ntdown;
}
break;
case DECREASE:
a->out[2 * i + 0] = a->in[2 * i + 0] * a->cdown[a->ntdown - a->count];
a->out[2 * i + 1] = a->in[2 * i + 1] * a->cdown[a->ntdown - a->count];
case AMSQState::DECREASE:
out[2 * i + 0] = (float) (in[2 * i + 0] * cdown[ntdown - count]);
out[2 * i + 1] = (float) (in[2 * i + 1] * cdown[ntdown - count]);
if (a->count-- == 0)
a->state = MUTED;
if (count-- == 0)
state = AMSQState::MUTED;
break;
}
}
}
else if (a->in != a->out)
else if (in != out)
{
std::copy( a->in, a->in + a->size * 2, a->out);
std::copy( in, in + size * 2, out);
}
}
void AMSQ::xamsqcap (AMSQ *a)
void AMSQ::xcap()
{
std::copy(a->trigger, a->trigger + a->size * 2, a->trigsig);
std::copy(trigger, trigger + size * 2, trigsig.begin());
}
void AMSQ::setBuffers_amsq (AMSQ *a, float* in, float* out, float* trigger)
void AMSQ::setBuffers(float* _in, float* _out, float* _trigger)
{
a->in = in;
a->out = out;
a->trigger = trigger;
in = _in;
out = _out;
trigger = _trigger;
}
void AMSQ::setSamplerate_amsq (AMSQ *a, int rate)
void AMSQ::setSamplerate(int _rate)
{
decalc_amsq (a);
a->rate = rate;
calc_amsq (a);
rate = _rate;
calc();
}
void AMSQ::setSize_amsq (AMSQ *a, int size)
void AMSQ::setSize(int _size)
{
decalc_amsq (a);
a->size = size;
calc_amsq (a);
size = _size;
calc();
}
/********************************************************************************************************
@ -253,53 +229,30 @@ void AMSQ::setSize_amsq (AMSQ *a, int size)
* *
********************************************************************************************************/
void AMSQ::SetAMSQRun (RXA& rxa, int run)
void AMSQ::setRun(int _run)
{
rxa.amsq.p->run = run;
run = _run;
}
void AMSQ::SetAMSQThreshold (RXA& rxa, double threshold)
void AMSQ::setThreshold(double _threshold)
{
double thresh = pow (10.0, threshold / 20.0);
rxa.amsq.p->tail_thresh = 0.9 * thresh;
rxa.amsq.p->unmute_thresh = thresh;
double thresh = pow (10.0, _threshold / 20.0);
tail_thresh = 0.9 * thresh;
unmute_thresh = thresh;
}
void AMSQ::SetAMSQMaxTail (RXA& rxa, double tail)
void AMSQ::setMaxTail(double _tail)
{
AMSQ *a;
a = rxa.amsq.p;
if (_tail < min_tail)
_tail = min_tail;
if (tail < a->min_tail)
tail = a->min_tail;
a->max_tail = tail;
max_tail = _tail;
}
/********************************************************************************************************
* *
* TXA Properties *
* *
********************************************************************************************************/
void AMSQ::SetAMSQRun (TXA& txa, int run)
{
txa.amsq.p->run = run;
}
void AMSQ::SetAMSQMutedGain (TXA& txa, double dBlevel)
void AMSQ::setMutedGain(double dBlevel)
{ // dBlevel is negative
AMSQ *a;
a = txa.amsq.p;
a->muted_gain = pow (10.0, dBlevel / 20.0);
compute_slews(a);
}
void AMSQ::SetAMSQThreshold (TXA& txa, double threshold)
{
double thresh = pow (10.0, threshold / 20.0);
txa.amsq.p->tail_thresh = 0.9 * thresh;
txa.amsq.p->unmute_thresh = thresh;
muted_gain = pow (10.0, dBlevel / 20.0);
compute_slews();
}
} // namespace WDSP

View File

@ -27,77 +27,84 @@ warren@wpratt.com
#ifndef _amsq_h
#define _amsq_h
#include <vector>
#include "export.h"
namespace WDSP {
class RXA;
class TXA;
class WDSP_API AMSQ
{
public:
int run; // 0 if squelch system is OFF; 1 if it's ON
int size; // size of input/output buffers
enum class AMSQState
{
MUTED,
INCREASE,
UNMUTED,
TAIL,
DECREASE
};
int run; // 0 if squelch system is OFF; 1 if it's ON
int size; // size of input/output buffers
float* in; // squelch input signal buffer
float* out; // squelch output signal buffer
float* trigger; // pointer to trigger data source
float* trigsig; // buffer containing trigger signal
double rate; // sample rate
double avtau; // time constant for averaging noise
std::vector<float> trigsig; // buffer containing trigger signal
double rate; // sample rate
double avtau; // time constant for averaging noise
double avm;
double onem_avm;
double avsig;
int state; // state machine control
AMSQState state; // state machine control
int count;
double tup;
double tdown;
int ntup;
int ntdown;
double* cup;
double* cdown;
std::vector<double> cup;
std::vector<double> cdown;
double tail_thresh;
double unmute_thresh;
double min_tail;
double max_tail;
double muted_gain;
static AMSQ* create_amsq (
int run,
int size,
float* in,
float* out,
float* trigger,
int rate,
double avtau,
double tup,
double tdown,
double tail_thresh,
double unmute_thresh,
double min_tail,
double max_tail,
double muted_gain
AMSQ (
int _run,
int _size,
float* _in,
float* _out,
float* _trigger,
int _rate,
double _avtau,
double _tup,
double _tdown,
double _tail_thresh,
double _unmute_thresh,
double _min_tail,
double _max_tail,
double _muted_gain
);
static void destroy_amsq (AMSQ *a);
static void flush_amsq (AMSQ *a);
static void xamsq (AMSQ *a);
static void xamsqcap (AMSQ *a);
static void setBuffers_amsq (AMSQ *a, float* in, float* out, float* trigger);
static void setSamplerate_amsq (AMSQ *a, int rate);
static void setSize_amsq (AMSQ *a, int size);
AMSQ(const AMSQ&) = delete;
AMSQ& operator=(const AMSQ& other) = delete;
~AMSQ() = default;
void flush();
void execute();
void xcap();
void setBuffers(float* in, float* out, float* trigger);
void setSamplerate(int rate);
void setSize(int size);
// RXA Properties
static void SetAMSQRun (RXA& rxa, int run);
static void SetAMSQThreshold (RXA& rxa, double threshold);
static void SetAMSQMaxTail (RXA& rxa, double tail);
// TXA Properties
static void SetAMSQRun (TXA& txa, int run);
static void SetAMSQMutedGain (TXA& txa, double dBlevel);
static void SetAMSQThreshold (TXA& txa, double threshold);
void setRun(int run);
void setThreshold(double threshold);
void setMaxTail(double tail);
void setMutedGain(double dBlevel);
private:
static void compute_slews(AMSQ *a);
static void calc_amsq(AMSQ *a);
static void decalc_amsq (AMSQ *a);
void compute_slews();
void calc();
};
} // namespace WDSP

View File

@ -27,7 +27,6 @@ warren@wpratt.com
#include "comm.hpp"
#include "anb.hpp"
#include "RXA.hpp"
#define MAX_TAU (0.01) // maximum transition time, signal<->zero (slew time)
#define MAX_ADVTIME (0.01) // maximum deadtime (zero output) in advance of detected noise
@ -35,261 +34,263 @@ warren@wpratt.com
namespace WDSP {
void ANB::initBlanker(ANB *a)
void ANB::initBlanker()
{
int i;
a->trans_count = (int)(a->tau * a->samplerate);
trans_count = (int)(tau * samplerate);
if (a->trans_count < 2)
a->trans_count = 2;
if (trans_count < 2)
trans_count = 2;
a->hang_count = (int)(a->hangtime * a->samplerate);
a->adv_count = (int)(a->advtime * a->samplerate);
a->count = 0;
a->in_idx = a->trans_count + a->adv_count;
a->out_idx = 0;
a->coef = PI / a->trans_count;
a->state = 0;
a->avg = 1.0;
a->power = 1.0;
a->backmult = exp(-1.0 / (a->samplerate * a->backtau));
a->ombackmult = 1.0 - a->backmult;
hang_count = (int)(hangtime * samplerate);
adv_count = (int)(advtime * samplerate);
count = 0;
in_idx = trans_count + adv_count;
out_idx = 0;
coef = PI / trans_count;
state = 0;
avg = 1.0;
power = 1.0;
backmult = exp(-1.0 / (samplerate * backtau));
ombackmult = 1.0 - backmult;
for (i = 0; i <= a->trans_count; i++)
a->wave[i] = 0.5 * cos(i * a->coef);
for (int i = 0; i <= trans_count; i++)
wave[i] = 0.5 * cos(i * coef);
std::fill(a->dline, a->dline + a->dline_size * 2, 0);
std::fill(dline.begin(), dline.end(), 0);
}
ANB* ANB::create_anb (
int run,
int buffsize,
float* in,
float* out,
double samplerate,
double tau,
double hangtime,
double advtime,
double backtau,
double threshold
)
ANB::ANB (
int _run,
int _buffsize,
float* _in,
float* _out,
double _samplerate,
double _tau,
double _hangtime,
double _advtime,
double _backtau,
double _threshold
) :
run(_run),
buffsize(_buffsize),
in(_in),
out(_out),
dline_size((int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1),
samplerate(_samplerate),
tau(_tau),
hangtime(_hangtime),
advtime(_advtime),
backtau(_backtau),
threshold(_threshold),
dtime(0),
htime(0),
itime(0),
atime(0)
{
ANB *a;
a = new ANB;
a->run = run;
a->buffsize = buffsize;
a->in = in;
a->out = out;
a->samplerate = samplerate;
a->tau = tau;
a->hangtime = hangtime;
a->advtime = advtime;
a->backtau = backtau;
a->threshold = threshold;
a->wave = new double[((int)(MAX_SAMPLERATE * MAX_TAU) + 1)];
a->dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1;
a->dline = new float[a->dline_size * 2];
initBlanker(a);
a->legacy = new float[2048 * 2]; /////////////// legacy interface - remove
return a;
if (tau < 0.0) {
tau = 0.0;
} else if (tau > MAX_TAU) {
tau = MAX_TAU;
}
if (hangtime < 0.0) {
hangtime = 0.0;
} else if (hangtime > MAX_ADVTIME) {
hangtime = MAX_ADVTIME;
}
if (advtime < 0.0) {
advtime = 0.0;
} else if (advtime > MAX_ADVTIME) {
advtime = MAX_ADVTIME;
}
if (samplerate < 0.0) {
samplerate = 0.0;
} else if (samplerate > MAX_SAMPLERATE) {
samplerate = MAX_SAMPLERATE;
}
wave.resize((int)(MAX_SAMPLERATE * MAX_TAU) + 1);
dline.resize(dline_size * 2);
initBlanker();
}
void ANB::destroy_anb (ANB *a)
void ANB::flush()
{
delete[] (a->legacy); /////////////// legacy interface - remove
delete[] (a->dline);
delete[] (a->wave);
delete (a);
initBlanker();
}
void ANB::flush_anb (ANB *a)
{
initBlanker (a);
}
void ANB::xanb (ANB *a)
void ANB::execute()
{
double scale;
double mag;
int i;
if (a->run)
if (run)
{
for (i = 0; i < a->buffsize; i++)
for (int i = 0; i < buffsize; i++)
{
double xr = a->in[2 * i + 0];
double xi = a->in[2 * i + 1];
double xr = in[2 * i + 0];
double xi = in[2 * i + 1];
mag = sqrt(xr*xr + xi*xi);
a->avg = a->backmult * a->avg + a->ombackmult * mag;
a->dline[2 * a->in_idx + 0] = a->in[2 * i + 0];
a->dline[2 * a->in_idx + 1] = a->in[2 * i + 1];
avg = backmult * avg + ombackmult * mag;
dline[2 * in_idx + 0] = in[2 * i + 0];
dline[2 * in_idx + 1] = in[2 * i + 1];
if (mag > (a->avg * a->threshold))
a->count = a->trans_count + a->adv_count;
if (mag > (avg * threshold))
count = trans_count + adv_count;
switch (a->state)
switch (state)
{
case 0:
a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0];
a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1];
out[2 * i + 0] = dline[2 * out_idx + 0];
out[2 * i + 1] = dline[2 * out_idx + 1];
if (a->count > 0)
if (count > 0)
{
a->state = 1;
a->dtime = 0;
a->power = 1.0;
state = 1;
dtime = 0;
power = 1.0;
}
break;
case 1:
scale = a->power * (0.5 + a->wave[a->dtime]);
a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0] * scale;
a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1] * scale;
scale = power * (0.5 + wave[dtime]);
out[2 * i + 0] = (float) (dline[2 * out_idx + 0] * scale);
out[2 * i + 1] = (float) (dline[2 * out_idx + 1] * scale);
if (++a->dtime > a->trans_count)
if (++dtime > trans_count)
{
a->state = 2;
a->atime = 0;
state = 2;
atime = 0;
}
break;
case 2:
a->out[2 * i + 0] = 0.0;
a->out[2 * i + 1] = 0.0;
out[2 * i + 0] = 0.0;
out[2 * i + 1] = 0.0;
if (++a->atime > a->adv_count)
a->state = 3;
if (++atime > adv_count)
state = 3;
break;
case 3:
if (a->count > 0)
a->htime = -a->count;
if (count > 0)
htime = -count;
a->out[2 * i + 0] = 0.0;
a->out[2 * i + 1] = 0.0;
out[2 * i + 0] = 0.0;
out[2 * i + 1] = 0.0;
if (++a->htime > a->hang_count)
if (++htime > hang_count)
{
a->state = 4;
a->itime = 0;
state = 4;
itime = 0;
}
break;
case 4:
scale = 0.5 - a->wave[a->itime];
a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0] * scale;
a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1] * scale;
scale = 0.5 - wave[itime];
out[2 * i + 0] = (float) (dline[2 * out_idx + 0] * scale);
out[2 * i + 1] = (float) (dline[2 * out_idx + 1] * scale);
if (a->count > 0)
if (count > 0)
{
a->state = 1;
a->dtime = 0;
a->power = scale;
state = 1;
dtime = 0;
power = scale;
}
else if (++a->itime > a->trans_count)
else if (++itime > trans_count)
{
a->state = 0;
state = 0;
}
break;
default:
break;
}
if (a->count > 0)
a->count--;
if (count > 0)
count--;
if (++a->in_idx == a->dline_size)
a->in_idx = 0;
if (++in_idx == dline_size)
in_idx = 0;
if (++a->out_idx == a->dline_size)
a->out_idx = 0;
if (++out_idx == dline_size)
out_idx = 0;
}
}
else if (a->in != a->out)
else if (in != out)
{
std::copy(a->in, a->in + a->buffsize * 2, a->out);
std::copy(in, in + buffsize * 2, out);
}
}
void ANB::setBuffers_anb (ANB *a, float* in, float* out)
void ANB::setBuffers(float* _in, float* _out)
{
a->in = in;
a->out = out;
in = _in;
out = _out;
}
void ANB::setSamplerate_anb (ANB *a, int rate)
void ANB::setSize(int size)
{
a->samplerate = rate;
initBlanker (a);
}
void ANB::setSize_anb (ANB *a, int size)
{
a->buffsize = size;
initBlanker (a);
buffsize = size;
initBlanker();
}
/********************************************************************************************************
* *
* RXA PROPERTIES *
* Common interface *
* *
********************************************************************************************************/
void ANB::SetANBRun (RXA& rxa, int run)
void ANB::setRun (int _run)
{
ANB *a = rxa.anb.p;
a->run = run;
run = _run;
}
void ANB::SetANBBuffsize (RXA& rxa, int size)
void ANB::setBuffsize (int size)
{
ANB *a = rxa.anb.p;
a->buffsize = size;
buffsize = size;
}
void ANB::SetANBSamplerate (RXA& rxa, int rate)
void ANB::setSamplerate (int rate)
{
ANB *a = rxa.anb.p;
a->samplerate = (double) rate;
initBlanker (a);
samplerate = (double) rate;
initBlanker();
}
void ANB::SetANBTau (RXA& rxa, double tau)
void ANB::setTau (double _tau)
{
ANB *a = rxa.anb.p;
a->tau = tau;
initBlanker (a);
tau = _tau;
initBlanker();
}
void ANB::SetANBHangtime (RXA& rxa, double time)
void ANB::setHangtime (double time)
{
ANB *a = rxa.anb.p;
a->hangtime = time;
initBlanker (a);
hangtime = time;
initBlanker();
}
void ANB::SetANBAdvtime (RXA& rxa, double time)
void ANB::setAdvtime (double time)
{
ANB *a = rxa.anb.p;
a->advtime = time;
initBlanker (a);
advtime = time;
initBlanker();
}
void ANB::SetANBBacktau (RXA& rxa, double tau)
void ANB::setBacktau (double _tau)
{
ANB *a = rxa.anb.p;
a->backtau = tau;
initBlanker (a);
backtau = _tau;
initBlanker();
}
void ANB::SetANBThreshold (RXA& rxa, double thresh)
void ANB::setThreshold (double thresh)
{
ANB *a = rxa.anb.p;
a->threshold = thresh;
threshold = thresh;
}
}

View File

@ -28,26 +28,28 @@ warren@wpratt.com
#ifndef wdsp_anb_h
#define wdsp_anb_h
#include <vector>
#include "export.h"
namespace WDSP {
class RXA;
class ANB
class WDSP_API ANB
{
public:
int run;
int buffsize; // size of input/output buffer
float* in; // input buffer
float* out; // output buffer
float* in; // input buffer
float* out; // output buffer
int dline_size; // length of delay line which is 'double dline[length][2]'
float *dline; // pointer to delay line
std::vector<float> dline; // delay line
double samplerate; // samplerate, used to convert times into sample counts
double tau; // transition time, signal<->zero
double hangtime; // time to stay at zero after noise is no longer detected
double advtime; // deadtime (zero output) in advance of detected noise
double backtau; // time constant used in averaging the magnitude of the input signal
double threshold; // triggers if (noise > threshold * average_signal_magnitude)
double *wave; // pointer to array holding transition waveform
std::vector<double> wave; // array holding transition waveform
int state; // state of the state machine
double avg; // average value of the signal magnitude
int dtime; // count when decreasing the signal magnitude
@ -64,9 +66,8 @@ public:
int count; // set each time a noise sample is detected, counts down
double backmult; // multiplier for waveform averaging
double ombackmult; // multiplier for waveform averaging
float *legacy;
static ANB* create_anb (
ANB(
int run,
int buffsize,
float* in,
@ -78,25 +79,26 @@ public:
double backtau,
double threshold
);
ANB(const ANB&) = delete;
ANB& operator=(const ANB& other) = delete;
~ANB() = default;
static void destroy_anb (ANB *a);
static void flush_anb (ANB *a);
static void xanb (ANB *a);
static void setBuffers_anb (ANB *a, float* in, float* out);
static void setSamplerate_anb (ANB *a, int rate);
static void setSize_anb (ANB *a, int size);
// RXA
static void SetANBRun (RXA& rxa, int run);
static void SetANBBuffsize (RXA& rxa, int size);
static void SetANBSamplerate (RXA& rxa, int rate);
static void SetANBTau (RXA& rxa, double tau);
static void SetANBHangtime (RXA& rxa, double time);
static void SetANBAdvtime (RXA& rxa, double time);
static void SetANBBacktau (RXA& rxa, double tau);
static void SetANBThreshold (RXA& rxa, double thresh);
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSize(int size);
// Common interface
void setRun (int run);
void setBuffsize (int size);
void setSamplerate (int rate);
void setTau (double tau);
void setHangtime (double time);
void setAdvtime (double time);
void setBacktau (double tau);
void setThreshold (double thresh);
private:
static void initBlanker(ANB *a); //////////// legacy interface - remove
void initBlanker();
};

View File

@ -32,148 +32,143 @@ warren@wpratt.com
#include "anr.hpp"
#include "anf.hpp"
#include "bandpass.hpp"
#include "RXA.hpp"
namespace WDSP {
ANF* ANF::create_anf(
int run,
int position,
int buff_size,
float *in_buff,
float *out_buff,
int dline_size,
int n_taps,
int delay,
double two_mu,
double gamma,
double lidx,
double lidx_min,
double lidx_max,
double ngamma,
double den_mult,
double lincr,
double ldecr
)
ANF::ANF(
int _run,
int _position,
int _buff_size,
float *_in_buff,
float *_out_buff,
int _dline_size,
int _n_taps,
int _delay,
double _two_mu,
double _gamma,
double _lidx,
double _lidx_min,
double _lidx_max,
double _ngamma,
double _den_mult,
double _lincr,
double _ldecr
) :
run(_run),
position(_position),
buff_size(_buff_size),
in_buff(_in_buff),
out_buff(_out_buff),
dline_size(_dline_size),
mask(_dline_size - 1),
n_taps(_n_taps),
delay(_delay),
two_mu(_two_mu),
gamma(_gamma),
in_idx(0),
lidx(_lidx),
lidx_min(_lidx_min),
lidx_max(_lidx_max),
ngamma(_ngamma),
den_mult(_den_mult),
lincr(_lincr),
ldecr(_ldecr)
{
ANF *a = new ANF;
a->run = run;
a->position = position;
a->buff_size = buff_size;
a->in_buff = in_buff;
a->out_buff = out_buff;
a->dline_size = dline_size;
a->mask = dline_size - 1;
a->n_taps = n_taps;
a->delay = delay;
a->two_mu = two_mu;
a->gamma = gamma;
a->in_idx = 0;
a->lidx = lidx;
a->lidx_min = lidx_min;
a->lidx_max = lidx_max;
a->ngamma = ngamma;
a->den_mult = den_mult;
a->lincr = lincr;
a->ldecr = ldecr;
memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
return a;
std::fill(d.begin(), d.end(), 0);
std::fill(w.begin(), w.end(), 0);
}
void ANF::destroy_anf (ANF *a)
void ANF::execute(int _position)
{
delete a;
}
int idx;
double c0;
double c1;
double y;
double error;
double sigma;
double inv_sigp;
double nel;
double nev;
void ANF::xanf(ANF *a, int position)
{
int i, j, idx;
double c0, c1;
double y, error, sigma, inv_sigp;
double nel, nev;
if (a->run && (a->position == position))
if (run && (position == _position))
{
for (i = 0; i < a->buff_size; i++)
for (int i = 0; i < buff_size; i++)
{
a->d[a->in_idx] = a->in_buff[2 * i + 0];
d[in_idx] = in_buff[2 * i + 0];
y = 0;
sigma = 0;
for (j = 0; j < a->n_taps; j++)
for (int j = 0; j < n_taps; j++)
{
idx = (a->in_idx + j + a->delay) & a->mask;
y += a->w[j] * a->d[idx];
sigma += a->d[idx] * a->d[idx];
idx = (in_idx + j + delay) & mask;
y += w[j] * d[idx];
sigma += d[idx] * d[idx];
}
inv_sigp = 1.0 / (sigma + 1e-10);
error = a->d[a->in_idx] - y;
error = d[in_idx] - y;
a->out_buff[2 * i + 0] = error;
a->out_buff[2 * i + 1] = 0.0;
out_buff[2 * i + 0] = (float) error;
out_buff[2 * i + 1] = 0.0;
if ((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0)
if ((nel = error * (1.0 - two_mu * sigma * inv_sigp)) < 0.0)
nel = -nel;
if ((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0)
if ((nev = d[in_idx] - (1.0 - two_mu * ngamma) * y - two_mu * error * sigma * inv_sigp) < 0.0)
nev = -nev;
if (nev < nel)
{
if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max;
if ((lidx += lincr) > lidx_max) lidx = lidx_max;
}
else
{
if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->lidx_min;
if ((lidx -= ldecr) < lidx_min) lidx = lidx_min;
}
a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult;
ngamma = gamma * (lidx * lidx) * (lidx * lidx) * den_mult;
c0 = 1.0 - a->two_mu * a->ngamma;
c1 = a->two_mu * error * inv_sigp;
c0 = 1.0 - two_mu * ngamma;
c1 = two_mu * error * inv_sigp;
for (j = 0; j < a->n_taps; j++)
for (int j = 0; j < n_taps; j++)
{
idx = (a->in_idx + j + a->delay) & a->mask;
a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
idx = (in_idx + j + delay) & mask;
w[j] = c0 * w[j] + c1 * d[idx];
}
a->in_idx = (a->in_idx + a->mask) & a->mask;
in_idx = (in_idx + mask) & mask;
}
}
else if (a->in_buff != a->out_buff)
else if (in_buff != out_buff)
{
std::copy(a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff);
std::copy(in_buff, in_buff + buff_size * 2, out_buff);
}
}
void ANF::flush_anf (ANF *a)
void ANF::flush()
{
memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
a->in_idx = 0;
std::fill(d.begin(), d.end(), 0);
std::fill(w.begin(), w.end(), 0);
in_idx = 0;
}
void ANF::setBuffers_anf (ANF *a, float* in, float* out)
void ANF::setBuffers(float* _in, float* _out)
{
a->in_buff = in;
a->out_buff = out;
in_buff = _in;
out_buff = _out;
}
void ANF::setSamplerate_anf (ANF *a, int)
void ANF::setSamplerate(int)
{
flush_anf (a);
flush();
}
void ANF::setSize_anf (ANF *a, int size)
void ANF::setSize(int _size)
{
a->buff_size = size;
flush_anf (a);
buff_size = _size;
flush();
}
/********************************************************************************************************
@ -182,65 +177,37 @@ void ANF::setSize_anf (ANF *a, int size)
* *
********************************************************************************************************/
void ANF::SetANFRun (RXA& rxa, int run)
void ANF::setVals(int _taps, int _delay, double _gain, double _leakage)
{
ANF *a = rxa.anf.p;
if (a->run != run)
{
RXA::bp1Check (
rxa,
rxa.amd.p->run,
rxa.snba.p->run,
rxa.emnr.p->run,
run,
rxa.anr.p->run
);
a->run = run;
RXA::bp1Set (rxa);
flush_anf (a);
}
n_taps = _taps;
delay = _delay;
two_mu = _gain; //try two_mu = 1e-4
gamma = _leakage; //try gamma = 0.10
flush();
}
void ANF::SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage)
void ANF::setTaps(int _taps)
{
rxa.anf.p->n_taps = taps;
rxa.anf.p->delay = delay;
rxa.anf.p->two_mu = gain; //try two_mu = 1e-4
rxa.anf.p->gamma = leakage; //try gamma = 0.10
flush_anf (rxa.anf.p);
n_taps = _taps;
flush();
}
void ANF::SetANFTaps (RXA& rxa, int taps)
void ANF::setDelay(int _delay)
{
rxa.anf.p->n_taps = taps;
flush_anf (rxa.anf.p);
delay = _delay;
flush();
}
void ANF::SetANFDelay (RXA& rxa, int delay)
void ANF::setGain(double _gain)
{
rxa.anf.p->delay = delay;
flush_anf (rxa.anf.p);
two_mu = _gain;
flush();
}
void ANF::SetANFGain (RXA& rxa, double gain)
void ANF::setLeakage(double _leakage)
{
rxa.anf.p->two_mu = gain;
flush_anf (rxa.anf.p);
}
void ANF::SetANFLeakage (RXA& rxa, double leakage)
{
rxa.anf.p->gamma = leakage;
flush_anf (rxa.anf.p);
}
void ANF::SetANFPosition (RXA& rxa, int position)
{
rxa.anf.p->position = position;
rxa.bp1.p->position = position;
flush_anf (rxa.anf.p);
gamma = _leakage;
flush();
}
} // namespace WDSP

View File

@ -28,14 +28,12 @@ warren@wpratt.com
#ifndef wdsp_anf_h
#define wdsp_anf_h
#include <array>
#include "export.h"
#define ANF_DLINE_SIZE 2048
namespace WDSP {
class RXA;
class WDSP_API ANF
{
public:
@ -50,8 +48,9 @@ public:
int delay;
double two_mu;
double gamma;
double d [ANF_DLINE_SIZE];
double w [ANF_DLINE_SIZE];
static const int ANF_DLINE_SIZE = 2048;
std::array<double, ANF_DLINE_SIZE> d;
std::array<double, ANF_DLINE_SIZE> w;
int in_idx;
double lidx;
double lidx_min;
@ -61,7 +60,7 @@ public:
double lincr;
double ldecr;
static ANF* create_anf(
ANF(
int run,
int position,
int buff_size,
@ -80,20 +79,21 @@ public:
double lincr,
double ldecr
);
static void destroy_anf (ANF *a);
static void flush_anf (ANF *a);
static void xanf (ANF *a, int position);
static void setBuffers_anf (ANF *a, float* in, float* out);
static void setSamplerate_anf (ANF *a, int rate);
static void setSize_anf (ANF *a, int size);
// RXA Properties
static void SetANFRun (RXA& rxa, int setit);
static void SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage);
static void SetANFTaps (RXA& rxa, int taps);
static void SetANFDelay (RXA& rxa, int delay);
static void SetANFGain (RXA& rxa, double gain);
static void SetANFLeakage (RXA& rxa, double leakage);
static void SetANFPosition (RXA& rxa, int position);
ANF(const ANF&) = delete;
ANF& operator=(const ANF& other) = delete;
~ANF() = default;
void flush();
void execute(int position);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// Public Properties
void setVals(int taps, int delay, double gain, double leakage);
void setTaps(int taps);
void setDelay(int delay);
void setGain(double gain);
void setLeakage(double leakage);
};
} // namespace WDSP

View File

@ -32,214 +32,182 @@ warren@wpratt.com
#include "emnr.hpp"
#include "anf.hpp"
#include "bandpass.hpp"
#include "RXA.hpp"
namespace WDSP {
ANR* ANR::create_anr (
int run,
int position,
int buff_size,
float *in_buff,
float *out_buff,
int dline_size,
int n_taps,
int delay,
double two_mu,
double gamma,
double lidx,
double lidx_min,
double lidx_max,
double ngamma,
double den_mult,
double lincr,
double ldecr
)
ANR::ANR(
int _run,
int _position,
int _buff_size,
float *_in_buff,
float *_out_buff,
int _dline_size,
int _n_taps,
int _delay,
double _two_mu,
double _gamma,
double _lidx,
double _lidx_min,
double _lidx_max,
double _ngamma,
double _den_mult,
double _lincr,
double _ldecr
) :
run(_run),
position(_position),
buff_size(_buff_size),
in_buff(_in_buff),
out_buff(_out_buff),
dline_size(_dline_size),
mask(_dline_size - 1),
n_taps(_n_taps),
delay(_delay),
two_mu(_two_mu),
gamma(_gamma),
in_idx(0),
lidx(_lidx),
lidx_min(_lidx_min),
lidx_max(_lidx_max),
ngamma(_ngamma),
den_mult(_den_mult),
lincr(_lincr),
ldecr(_ldecr)
{
ANR *a = new ANR;
a->run = run;
a->position = position;
a->buff_size = buff_size;
a->in_buff = in_buff;
a->out_buff = out_buff;
a->dline_size = dline_size;
a->mask = dline_size - 1;
a->n_taps = n_taps;
a->delay = delay;
a->two_mu = two_mu;
a->gamma = gamma;
a->in_idx = 0;
a->lidx = lidx;
a->lidx_min = lidx_min;
a->lidx_max = lidx_max;
a->ngamma = ngamma;
a->den_mult = den_mult;
a->lincr = lincr;
a->ldecr = ldecr;
memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE);
memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE);
return a;
std::fill(d.begin(), d.end(), 0);
std::fill(w.begin(), w.end(), 0);
}
void ANR::destroy_anr (ANR *a)
void ANR::execute(int _position)
{
delete a;
}
int idx;
double c0;
double c1;
double y;
double error;
double sigma;
double inv_sigp;
double nel;
double nev;
void ANR::xanr (ANR *a, int position)
{
int i, j, idx;
double c0, c1;
double y, error, sigma, inv_sigp;
double nel, nev;
if (a->run && (a->position == position))
if (run && (position == _position))
{
for (i = 0; i < a->buff_size; i++)
for (int i = 0; i < buff_size; i++)
{
a->d[a->in_idx] = a->in_buff[2 * i + 0];
d[in_idx] = in_buff[2 * i + 0];
y = 0;
sigma = 0;
for (j = 0; j < a->n_taps; j++)
for (int j = 0; j < n_taps; j++)
{
idx = (a->in_idx + j + a->delay) & a->mask;
y += a->w[j] * a->d[idx];
sigma += a->d[idx] * a->d[idx];
idx = (in_idx + j + delay) & mask;
y += w[j] * d[idx];
sigma += d[idx] * d[idx];
}
inv_sigp = 1.0 / (sigma + 1e-10);
error = a->d[a->in_idx] - y;
error = d[in_idx] - y;
a->out_buff[2 * i + 0] = y;
a->out_buff[2 * i + 1] = 0.0;
out_buff[2 * i + 0] = (float) y;
out_buff[2 * i + 1] = 0.0;
if ((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0)
if ((nel = error * (1.0 - two_mu * sigma * inv_sigp)) < 0.0)
nel = -nel;
if ((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0)
if ((nev = d[in_idx] - (1.0 - two_mu * ngamma) * y - two_mu * error * sigma * inv_sigp) < 0.0)
nev = -nev;
if (nev < nel)
{
if ((a->lidx += a->lincr) > a->lidx_max)
a->lidx = a->lidx_max;
if ((lidx += lincr) > lidx_max)
lidx = lidx_max;
}
else
{
if ((a->lidx -= a->ldecr) < a->lidx_min)
a->lidx = a->lidx_min;
if ((lidx -= ldecr) < lidx_min)
lidx = lidx_min;
}
a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult;
c0 = 1.0 - a->two_mu * a->ngamma;
c1 = a->two_mu * error * inv_sigp;
ngamma = gamma * (lidx * lidx) * (lidx * lidx) * den_mult;
c0 = 1.0 - two_mu * ngamma;
c1 = two_mu * error * inv_sigp;
for (j = 0; j < a->n_taps; j++)
for (int j = 0; j < n_taps; j++)
{
idx = (a->in_idx + j + a->delay) & a->mask;
a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
idx = (in_idx + j + delay) & mask;
w[j] = c0 * w[j] + c1 * d[idx];
}
a->in_idx = (a->in_idx + a->mask) & a->mask;
in_idx = (in_idx + mask) & mask;
}
}
else if (a->in_buff != a->out_buff)
else if (in_buff != out_buff)
{
std::copy(a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff);
std::copy(in_buff, in_buff + buff_size * 2, out_buff);
}
}
void ANR::flush_anr (ANR *a)
void ANR::flush()
{
memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE);
memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE);
a->in_idx = 0;
std::fill(d.begin(), d.end(), 0);
std::fill(w.begin(), w.end(), 0);
in_idx = 0;
}
void ANR::setBuffers_anr (ANR *a, float* in, float* out)
void ANR::setBuffers(float* _in, float* _out)
{
a->in_buff = in;
a->out_buff = out;
in_buff = _in;
out_buff = _out;
}
void ANR::setSamplerate_anr (ANR *a, int)
void ANR::setSamplerate(int)
{
flush_anr(a);
flush();
}
void ANR::setSize_anr (ANR *a, int size)
void ANR::setSize(int _size)
{
a->buff_size = size;
flush_anr(a);
buff_size = _size;
flush();
}
/********************************************************************************************************
* *
* RXA Properties *
* Public Properties *
* *
********************************************************************************************************/
void ANR::SetANRRun (RXA& rxa, int run)
void ANR::setVals(int _taps, int _delay, double _gain, double _leakage)
{
ANR *a = rxa.anr.p;
if (a->run != run)
{
RXA::bp1Check (
rxa,
rxa.amd.p->run,
rxa.snba.p->run,
rxa.emnr.p->run,
rxa.anf.p->run,
run
);
a->run = run;
RXA::bp1Set (rxa);
flush_anr (a);
}
n_taps = _taps;
delay = _delay;
two_mu = _gain;
gamma = _leakage;
flush();
}
void ANR::SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage)
void ANR::setTaps(int _taps)
{
rxa.anr.p->n_taps = taps;
rxa.anr.p->delay = delay;
rxa.anr.p->two_mu = gain;
rxa.anr.p->gamma = leakage;
flush_anr (rxa.anr.p);
n_taps = _taps;
flush();
}
void ANR::SetANRTaps (RXA& rxa, int taps)
void ANR::setDelay(int _delay)
{
rxa.anr.p->n_taps = taps;
flush_anr (rxa.anr.p);
delay = _delay;
flush();
}
void ANR::SetANRDelay (RXA& rxa, int delay)
void ANR::setGain(double _gain)
{
rxa.anr.p->delay = delay;
flush_anr (rxa.anr.p);
two_mu = _gain;
flush();
}
void ANR::SetANRGain (RXA& rxa, double gain)
void ANR::setLeakage(double _leakage)
{
rxa.anr.p->two_mu = gain;
flush_anr (rxa.anr.p);
}
void ANR::SetANRLeakage (RXA& rxa, double leakage)
{
rxa.anr.p->gamma = leakage;
flush_anr (rxa.anr.p);
}
void ANR::SetANRPosition (RXA& rxa, int position)
{
rxa.anr.p->position = position;
rxa.bp1.p->position = position;
flush_anr (rxa.anr.p);
gamma = _leakage;
flush();
}
} // namespace WDSP

View File

@ -28,14 +28,12 @@ warren@wpratt.com
#ifndef wdsp_anr_h
#define wdsp_anr_h
#include <array>
#include "export.h"
#define ANR_DLINE_SIZE 2048
namespace WDSP {
class RXA;
class WDSP_API ANR
{
public:
@ -50,8 +48,9 @@ public:
int delay;
double two_mu;
double gamma;
double d [ANR_DLINE_SIZE];
double w [ANR_DLINE_SIZE];
static const int ANR_DLINE_SIZE = 2048;
std::array<double, ANR_DLINE_SIZE> d;
std::array<double, ANR_DLINE_SIZE> w;
int in_idx;
double lidx;
@ -62,7 +61,7 @@ public:
double lincr;
double ldecr;
static ANR* create_anr (
ANR(
int run,
int position,
int buff_size,
@ -81,21 +80,21 @@ public:
double lincr,
double ldecr
);
ANR(const ANR&) = delete;
ANR& operator=(const ANR& other) = delete;
~ANR() = default;
static void destroy_anr (ANR *a);
static void flush_anr (ANR *a);
static void xanr (ANR *a, int position);
static void setBuffers_anr (ANR *a, float* in, float* out);
static void setSamplerate_anr (ANR *a, int rate);
static void setSize_anr (ANR *a, int size);
void flush();
void execute(int position);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// RXA Properties
static void SetANRRun (RXA& rxa, int setit);
static void SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage);
static void SetANRTaps (RXA& rxa, int taps);
static void SetANRDelay (RXA& rxa, int delay);
static void SetANRGain (RXA& rxa, double gain);
static void SetANRLeakage (RXA& rxa, double leakage);
static void SetANRPosition (RXA& rxa, int position);
void setVals(int taps, int delay, double gain, double leakage);
void setTaps(int taps);
void setDelay(int delay);
void setGain(double gain);
void setLeakage(double leakage);
};
} // namespace WDSP

View File

@ -29,8 +29,6 @@ warren@wpratt.com
#include "bandpass.hpp"
#include "fir.hpp"
#include "fircore.hpp"
#include "RXA.hpp"
#include "TXA.hpp"
namespace WDSP {
@ -40,144 +38,146 @@ namespace WDSP {
* *
********************************************************************************************************/
BANDPASS* BANDPASS::create_bandpass (
int run,
int position,
int size,
int nc,
int mp,
float* in,
float* out,
double f_low,
double f_high,
int samplerate,
int wintype,
double gain
)
{
BANDPASS::BANDPASS(
int _run,
int _position,
int _size,
int _nc,
int _mp,
float* _in,
float* _out,
double _f_low,
double _f_high,
int _samplerate,
int _wintype,
double _gain
) :
// NOTE: 'nc' must be >= 'size'
BANDPASS *a = new BANDPASS;
a->run = run;
a->position = position;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->f_low = f_low;
a->f_high = f_high;
a->samplerate = samplerate;
a->wintype = wintype;
a->gain = gain;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
run(_run),
position(_position),
size(_size),
nc(_nc),
mp(_mp),
in(_in),
out(_out),
f_low(_f_low),
f_high(_f_high),
samplerate(_samplerate),
wintype(_wintype),
gain(_gain)
{
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
f_low,
f_high,
samplerate,
wintype,
1,
a->gain / (double)(2 * a->size)
gain / (double)(2 * size)
);
a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
delete[] impulse;
return a;
fircore = new FIRCORE(size, in, out, mp, impulse);
}
void BANDPASS::destroy_bandpass (BANDPASS *a)
BANDPASS::~BANDPASS()
{
FIRCORE::destroy_fircore (a->p);
delete a;
delete (fircore);
}
void BANDPASS::flush_bandpass (BANDPASS *a)
void BANDPASS::flush()
{
FIRCORE::flush_fircore (a->p);
fircore->flush();
}
void BANDPASS::xbandpass (BANDPASS *a, int pos)
void BANDPASS::execute(int pos)
{
if (a->run && a->position == pos)
FIRCORE::xfircore (a->p);
else if (a->out != a->in)
std::copy( a->in, a->in + a->size * 2, a->out);
if (run && position == pos)
fircore->execute();
else if (out != in)
std::copy(in, in + size * 2, out);
}
void BANDPASS::setBuffers_bandpass (BANDPASS *a, float* in, float* out)
void BANDPASS::setBuffers(float* _in, float* _out)
{
a->in = in;
a->out = out;
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
in = _in;
out = _out;
fircore->setBuffers(in, out);
}
void BANDPASS::setSamplerate_bandpass (BANDPASS *a, int rate)
void BANDPASS::setSamplerate(int _rate)
{
a->samplerate = rate;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
samplerate = _rate;
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
f_low,
f_high,
samplerate,
wintype,
1,
a->gain / (float)(2 * a->size)
gain / (double) (2 * size)
);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] impulse;
fircore->setImpulse(impulse, 1);
}
void BANDPASS::setSize_bandpass (BANDPASS *a, int size)
void BANDPASS::setSize(int _size)
{
// NOTE: 'size' must be <= 'nc'
a->size = size;
FIRCORE::setSize_fircore (a->p, a->size);
size = _size;
fircore->setSize(size);
// recalc impulse because scale factor is a function of size
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
f_low,
f_high,
samplerate,
wintype,
1,
a->gain / (float)(2 * a->size)
gain / (double) (2 * size)
);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
fircore->setImpulse(impulse, 1);
}
void BANDPASS::setGain_bandpass (BANDPASS *a, double gain, int update)
void BANDPASS::setGain(double _gain, int _update)
{
a->gain = gain;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
gain = _gain;
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
f_low,
f_high,
samplerate,
wintype,
1,
a->gain / (double)(2 * a->size)
gain / (double) (2 * size)
);
FIRCORE::setImpulse_fircore (a->p, impulse, update);
delete[] (impulse);
fircore->setImpulse(impulse, _update);
}
void BANDPASS::CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, double gain)
void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain)
{
if ((a->f_low != f_low) || (a->f_high != f_high) || (a->gain != gain))
if ((f_low != _f_low) || (f_high != _f_high) || (gain != _gain))
{
a->f_low = f_low;
a->f_high = f_high;
a->gain = gain;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
f_low = _f_low;
f_high = _f_high;
gain = _gain;
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
f_low,
f_high,
samplerate,
wintype,
1,
a->gain / (double)(2 * a->size)
gain / (double)(2 * size)
);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
fircore->setImpulse(impulse, 1);
}
}
@ -187,62 +187,56 @@ void BANDPASS::CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, dou
* *
********************************************************************************************************/
void BANDPASS::SetBandpassFreqs (RXA& rxa, double f_low, double f_high)
void BANDPASS::setBandpassFreqs(double _f_low, double _f_high)
{
BANDPASS *a = rxa.bp1.p;
if ((f_low != a->f_low) || (f_high != a->f_high))
if ((_f_low != f_low) || (_f_high != f_high))
{
float* impulse = FIR::fir_bandpass (
a->nc,
f_low,
f_high,
a->samplerate,
a->wintype,
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
_f_low,
_f_high,
samplerate,
wintype,
1,
a->gain / (double)(2 * a->size)
gain / (double)(2 * size)
);
FIRCORE::setImpulse_fircore (a->p, impulse, 0);
delete[] (impulse);
a->f_low = f_low;
a->f_high = f_high;
FIRCORE::setUpdate_fircore (a->p);
fircore->setImpulse(impulse, 0);
f_low = _f_low;
f_high = _f_high;
fircore->setUpdate();
}
}
void BANDPASS::SetBandpassNC (RXA& rxa, int nc)
void BANDPASS::SetBandpassNC(int _nc)
{
// NOTE: 'nc' must be >= 'size'
BANDPASS *a;
a = rxa.bp1.p;
if (nc != a->nc)
if (_nc != nc)
{
a->nc = nc;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
nc = _nc;
std::vector<float> impulse;
FIR::fir_bandpass (
impulse,
nc,
f_low,
f_high,
samplerate,
wintype,
1,
a->gain / (double)(2 * a->size)
gain / (double)( 2 * size)
);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
fircore->setNc(impulse);
}
}
void BANDPASS::SetBandpassMP (RXA& rxa, int mp)
void BANDPASS::SetBandpassMP(int _mp)
{
BANDPASS *a;
a = rxa.bp1.p;
if (mp != a->mp)
if (_mp != mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
mp = _mp;
fircore->setMp(mp);
}
}
@ -252,125 +246,4 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp)
* *
********************************************************************************************************/
//PORT
//void SetTXABandpassFreqs (int channel, float f_low, float f_high)
//{
// float* impulse;
// BANDPASS a;
// a = txa.bp0.p;
// if ((f_low != a->f_low) || (f_high != a->f_high))
// {
// a->f_low = f_low;
// a->f_high = f_high;
// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size));
// setImpulse_fircore (a->p, impulse, 1);
// delete[] (impulse);
// }
// a = txa.bp1.p;
// if ((f_low != a->f_low) || (f_high != a->f_high))
// {
// a->f_low = f_low;
// a->f_high = f_high;
// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size));
// setImpulse_fircore (a->p, impulse, 1);
// delete[] (impulse);
// }
// a = txa.bp2.p;
// if ((f_low != a->f_low) || (f_high != a->f_high))
// {
// a->f_low = f_low;
// a->f_high = f_high;
// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size));
// setImpulse_fircore (a->p, impulse, 1);
// delete[] (impulse);
// }
//}
void BANDPASS::SetBandpassNC (TXA& txa, int nc)
{
// NOTE: 'nc' must be >= 'size'
BANDPASS *a;
a = txa.bp0.p;
if (a->nc != nc)
{
a->nc = nc;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
1,
a->gain / (double)(2 * a->size)
);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
}
a = txa.bp1.p;
if (a->nc != nc)
{
a->nc = nc;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
1,
a->gain / (double)(2 * a->size)
);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
}
a = txa.bp2.p;
if (a->nc != nc)
{
a->nc = nc;
float* impulse = FIR::fir_bandpass (
a->nc,
a->f_low,
a->f_high,
a->samplerate,
a->wintype,
1,
a->gain / (double)(2 * a->size)
);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
}
}
void BANDPASS::SetBandpassMP (TXA& txa, int mp)
{
BANDPASS *a;
a = txa.bp0.p;
if (mp != a->mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
}
a = txa.bp1.p;
if (mp != a->mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
}
a = txa.bp2.p;
if (mp != a->mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
}
}
} // namespace WDSP

View File

@ -46,8 +46,6 @@ warren@wpratt.com
namespace WDSP {
class FIRCORE;
class RXA;
class TXA;
class WDSP_API BANDPASS
{
@ -64,9 +62,9 @@ public:
double samplerate;
int wintype;
double gain;
FIRCORE *p;
FIRCORE *fircore;
static BANDPASS *create_bandpass (
BANDPASS(
int run,
int position,
int size,
@ -80,21 +78,21 @@ public:
int wintype,
double gain
);
static void destroy_bandpass (BANDPASS *a);
static void flush_bandpass (BANDPASS *a);
static void xbandpass (BANDPASS *a, int pos);
static void setBuffers_bandpass (BANDPASS *a, float* in, float* out);
static void setSamplerate_bandpass (BANDPASS *a, int rate);
static void setSize_bandpass (BANDPASS *a, int size);
static void setGain_bandpass (BANDPASS *a, double gain, int update);
static void CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, double gain);
BANDPASS(const BANDPASS&) = delete;
BANDPASS& operator=(const BANDPASS& other) = delete;
~BANDPASS();
void flush();
void execute(int pos);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
void setGain(double gain, int update);
void calcBandpassFilter(double f_low, double f_high, double gain);
// RXA Prototypes
static void SetBandpassFreqs (RXA& rxa, double f_low, double f_high);
static void SetBandpassNC (RXA& rxa, int nc);
static void SetBandpassMP (RXA& rxa, int mp);
// TXA Prototypes
static void SetBandpassNC (TXA& txa, int nc);
static void SetBandpassMP (TXA& txa, int mp);
void setBandpassFreqs(double f_low, double f_high);
void SetBandpassNC(int nc);
void SetBandpassMP(int mp);
};
} // namespace WDSP

View File

@ -30,138 +30,102 @@ warren@wpratt.com
namespace WDSP {
BLDR* BLDR::create_builder(int points, int ints)
BLDR::BLDR(int points, int ints)
{
// for the create function, 'points' and 'ints' are the MAXIMUM values that will be encountered
BLDR *a = new BLDR;
a->catxy = new float[2 * points]; // (float*)malloc0(2 * points * sizeof(float));
a->sx = new float[points]; // (float*)malloc0( points * sizeof(float));
a->sy = new float[points]; // (float*)malloc0( points * sizeof(float));
a->h = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->p = new int[ints]; // (int*) malloc0( ints * sizeof(int));
a->np = new int[ints]; // (int*) malloc0( ints * sizeof(int));
a->taa = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tab = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tag = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tad = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tbb = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tbg = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tbd = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tgg = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tgd = new float[ints]; // (float*)malloc0( ints * sizeof(float));
a->tdd = new float[ints]; // (float*)malloc0( ints * sizeof(float));
catxy = new double[2 * points];
sx.resize(points);
sy.resize(points);
h .resize(ints);
p.resize(ints);
np.resize(ints);
taa.resize(ints);
tab.resize(ints);
tag.resize(ints);
tad.resize(ints);
tbb.resize(ints);
tbg.resize(ints);
tbd.resize(ints);
tgg.resize(ints);
tgd.resize(ints);
tdd.resize(ints);
int nsize = 3 * ints + 1;
int intp1 = ints + 1;
int intm1 = ints - 1;
a->A = new float[intp1 * intp1]; // (float*)malloc0(intp1 * intp1 * sizeof(float));
a->B = new float[intp1 * intp1]; // (float*)malloc0(intp1 * intp1 * sizeof(float));
a->C = new float[intp1 * intp1]; // (float*)malloc0(intm1 * intp1 * sizeof(float));
a->D = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float));
a->E = new float[intp1 * intp1]; // (float*)malloc0(intp1 * intp1 * sizeof(float));
a->F = new float[intm1 * intp1]; // (float*)malloc0(intm1 * intp1 * sizeof(float));
a->G = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float));
a->MAT = new float[nsize * nsize]; // (float*)malloc0(nsize * nsize * sizeof(float));
a->RHS = new float[nsize]; // (float*)malloc0(nsize * sizeof(float));
a->SLN = new float[nsize]; // (float*)malloc0(nsize * sizeof(float));
a->z = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float));
a->zp = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float));
a->wrk = new float[nsize]; // (float*)malloc0(nsize * sizeof(float));
a->ipiv = new int[nsize]; // (int*) malloc0(nsize * sizeof(int));
return a;
A .resize(intp1 * intp1);
B .resize(intp1 * intp1);
C .resize(intp1 * intp1);
D .resize(intp1);
E .resize(intp1 * intp1);
F .resize(intm1 * intp1);
G .resize(intp1);
MAT.resize(nsize * nsize);
RHS.resize(nsize);
SLN.resize(nsize);
z .resize(intp1);
zp.resize(intp1);
wrk.resize(nsize);
ipiv.resize(nsize);
}
void BLDR::destroy_builder(BLDR *a)
BLDR::~BLDR()
{
delete[](a->ipiv);
delete[](a->wrk);
delete[](a->catxy);
delete[](a->sx);
delete[](a->sy);
delete[](a->h);
delete[](a->p);
delete[](a->np);
delete[](a->taa);
delete[](a->tab);
delete[](a->tag);
delete[](a->tad);
delete[](a->tbb);
delete[](a->tbg);
delete[](a->tbd);
delete[](a->tgg);
delete[](a->tgd);
delete[](a->tdd);
delete[](a->A);
delete[](a->B);
delete[](a->C);
delete[](a->D);
delete[](a->E);
delete[](a->F);
delete[](a->G);
delete[](a->MAT);
delete[](a->RHS);
delete[](a->SLN);
delete[](a->z);
delete[](a->zp);
delete(a);
delete[]catxy;
}
void BLDR::flush_builder(BLDR *a, int points, int ints)
void BLDR::flush(int points)
{
memset(a->catxy, 0, 2 * points * sizeof(float));
memset(a->sx, 0, points * sizeof(float));
memset(a->sy, 0, points * sizeof(float));
memset(a->h, 0, ints * sizeof(float));
memset(a->p, 0, ints * sizeof(int));
memset(a->np, 0, ints * sizeof(int));
memset(a->taa, 0, ints * sizeof(float));
memset(a->tab, 0, ints * sizeof(float));
memset(a->tag, 0, ints * sizeof(float));
memset(a->tad, 0, ints * sizeof(float));
memset(a->tbb, 0, ints * sizeof(float));
memset(a->tbg, 0, ints * sizeof(float));
memset(a->tbd, 0, ints * sizeof(float));
memset(a->tgg, 0, ints * sizeof(float));
memset(a->tgd, 0, ints * sizeof(float));
memset(a->tdd, 0, ints * sizeof(float));
int nsize = 3 * ints + 1;
int intp1 = ints + 1;
int intm1 = ints - 1;
memset(a->A, 0, intp1 * intp1 * sizeof(float));
memset(a->B, 0, intp1 * intp1 * sizeof(float));
memset(a->C, 0, intm1 * intp1 * sizeof(float));
memset(a->D, 0, intp1 * sizeof(float));
memset(a->E, 0, intp1 * intp1 * sizeof(float));
memset(a->F, 0, intm1 * intp1 * sizeof(float));
memset(a->G, 0, intp1 * sizeof(float));
memset(a->MAT, 0, nsize * nsize * sizeof(float));
memset(a->RHS, 0, nsize * sizeof(float));
memset(a->SLN, 0, nsize * sizeof(float));
memset(a->z, 0, intp1 * sizeof(float));
memset(a->zp, 0, intp1 * sizeof(float));
memset(a->wrk, 0, nsize * sizeof(float));
memset(a->ipiv, 0, nsize * sizeof(int));
memset(catxy, 0, 2 * points * sizeof(double));
std::fill(sx.begin(), sx.end(), 0);
std::fill(sy.begin(), sy.end(), 0);
std::fill(h.begin(), h.end(), 0);
std::fill(p.begin(), p.end(), 0);
std::fill(np.begin(), np.end(), 0);
std::fill(taa.begin(), taa.end(), 0);
std::fill(tab.begin(), tab.end(), 0);
std::fill(tag.begin(), tag.end(), 0);
std::fill(tad.begin(), tad.end(), 0);
std::fill(tbb.begin(), tbb.end(), 0);
std::fill(tbg.begin(), tbg.end(), 0);
std::fill(tbd.begin(), tbd.end(), 0);
std::fill(tgg.begin(), tgg.end(), 0);
std::fill(tgd.begin(), tgd.end(), 0);
std::fill(tdd.begin(), tdd.end(), 0);
std::fill(A.begin(), A.end(), 0);
std::fill(B.begin(), B.end(), 0);
std::fill(C.begin(), C.end(), 0);
std::fill(D.begin(), D.end(), 0);
std::fill(E.begin(), E.end(), 0);
std::fill(F.begin(), F.end(), 0);
std::fill(G.begin(), G.end(), 0);
std::fill(MAT.begin(), MAT.end(), 0);
std::fill(RHS.begin(), RHS.end(), 0);
std::fill(SLN.begin(), SLN.end(), 0);
std::fill(z.begin(), z.end(), 0);
std::fill(zp.begin(), zp.end(), 0);
std::fill(wrk.begin(), wrk.end(), 0);
std::fill(ipiv.begin(), ipiv.end(), 0);
}
int BLDR::fcompare(const void* a, const void* b)
{
if (*(float*)a < *(float*)b)
if (*(double*)a < *(double*)b)
return -1;
else if (*(float*)a == *(float*)b)
else if (*(double*)a == *(double*)b)
return 0;
else
return 1;
}
void BLDR::decomp(int n, float* a, int* piv, int* info, float* wrk)
void BLDR::decomp(int n, std::vector<double>& a, std::vector<int>& piv, int* info, std::vector<double>& wrk)
{
int i, j, k;
int i;
int j;
int t_piv;
float m_row, mt_row, m_col, mt_col;
double m_row;
double mt_row;
double m_col;
double mt_col;
*info = 0;
for (i = 0; i < n; i++)
{
@ -180,7 +144,7 @@ void BLDR::decomp(int n, float* a, int* piv, int* info, float* wrk)
}
wrk[i] = m_row;
}
for (k = 0; k < n - 1; k++)
for (int k = 0; k < n - 1; k++)
{
j = k;
m_col = a[n * piv[k] + k] / wrk[piv[k]];
@ -216,10 +180,11 @@ cleanup:
return;
}
void BLDR::dsolve(int n, float* a, int* piv, float* b, float* x)
void BLDR::dsolve(int n, std::vector<double>& a, std::vector<int>& piv, std::vector<double>& b, std::vector<double>& x)
{
int j, k;
float sum;
int j;
int k;
double sum;
for (k = 0; k < n; k++)
{
@ -238,7 +203,7 @@ void BLDR::dsolve(int n, float* a, int* piv, float* b, float* x)
}
}
void BLDR::cull(int* n, int ints, float* x, float* t, float ptol)
void BLDR::cull(int* n, int ints, std::vector<double>& x, const double* t, double ptol)
{
int k = 0;
int i = *n;
@ -255,28 +220,36 @@ void BLDR::cull(int* n, int ints, float* x, float* t, float ptol)
*n -= k;
}
void BLDR::xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t, int* info, float* c, float ptol)
void BLDR::execute(int points, const double* x, const double* y, int ints, const double* t, int* info, double* c, double ptol)
{
float u, v, alpha, beta, gamma, delta;
double u;
double v;
double alpha;
double beta;
double gamma;
double delta;
int nsize = 3 * ints + 1;
int intp1 = ints + 1;
int intm1 = ints - 1;
int i, j, k, m;
int i;
int j;
int k;
int m;
int dinfo;
flush_builder(a, points, ints);
flush(points);
for (i = 0; i < points; i++)
{
a->catxy[2 * i + 0] = x[i];
a->catxy[2 * i + 1] = y[i];
catxy[2 * i + 0] = x[i];
catxy[2 * i + 1] = y[i];
}
qsort(a->catxy, points, 2 * sizeof(float), fcompare);
qsort(catxy, points, 2 * sizeof(double), fcompare);
for (i = 0; i < points; i++)
{
a->sx[i] = a->catxy[2 * i + 0];
a->sy[i] = a->catxy[2 * i + 1];
sx[i] = catxy[2 * i + 0];
sy[i] = catxy[2 * i + 1];
}
cull(&points, ints, a->sx, t, ptol);
if (points <= 0 || a->sx[points - 1] > t[ints])
cull(&points, ints, sx, t, ptol);
if (points <= 0 || sx[points - 1] > t[ints])
{
*info = -1000;
goto cleanup;
@ -284,101 +257,101 @@ void BLDR::xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t,
else *info = 0;
for (j = 0; j < ints; j++)
a->h[j] = t[j + 1] - t[j];
a->p[0] = 0;
h[j] = t[j + 1] - t[j];
p[0] = 0;
j = 0;
for (i = 0; i < points; i++)
{
if (a->sx[i] <= t[j + 1])
a->np[j]++;
if (sx[i] <= t[j + 1])
np[j]++;
else
{
a->p[++j] = i;
while (a->sx[i] > t[j + 1])
a->p[++j] = i;
a->np[j] = 1;
p[++j] = i;
while (sx[i] > t[j + 1])
p[++j] = i;
np[j] = 1;
}
}
for (i = 0; i < ints; i++)
for (j = a->p[i]; j < a->p[i] + a->np[i]; j++)
for (j = p[i]; j < p[i] + np[i]; j++)
{
u = (a->sx[j] - t[i]) / a->h[i];
u = (sx[j] - t[i]) / h[i];
v = u - 1.0;
alpha = (2.0 * u + 1.0) * v * v;
beta = u * u * (1.0 - 2.0 * v);
gamma = a->h[i] * u * v * v;
delta = a->h[i] * u * u * v;
a->taa[i] += alpha * alpha;
a->tab[i] += alpha * beta;
a->tag[i] += alpha * gamma;
a->tad[i] += alpha * delta;
a->tbb[i] += beta * beta;
a->tbg[i] += beta * gamma;
a->tbd[i] += beta * delta;
a->tgg[i] += gamma * gamma;
a->tgd[i] += gamma * delta;
a->tdd[i] += delta * delta;
a->D[i + 0] += 2.0 * a->sy[j] * alpha;
a->D[i + 1] += 2.0 * a->sy[j] * beta;
a->G[i + 0] += 2.0 * a->sy[j] * gamma;
a->G[i + 1] += 2.0 * a->sy[j] * delta;
gamma = h[i] * u * v * v;
delta = h[i] * u * u * v;
taa[i] += alpha * alpha;
tab[i] += alpha * beta;
tag[i] += alpha * gamma;
tad[i] += alpha * delta;
tbb[i] += beta * beta;
tbg[i] += beta * gamma;
tbd[i] += beta * delta;
tgg[i] += gamma * gamma;
tgd[i] += gamma * delta;
tdd[i] += delta * delta;
D[i + 0] += 2.0 * sy[j] * alpha;
D[i + 1] += 2.0 * sy[j] * beta;
G[i + 0] += 2.0 * sy[j] * gamma;
G[i + 1] += 2.0 * sy[j] * delta;
}
for (i = 0; i < ints; i++)
{
a->A[(i + 0) * intp1 + (i + 0)] += 2.0 * a->taa[i];
a->A[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tbb[i];
a->A[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tab[i];
a->A[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tab[i];
a->B[(i + 0) * intp1 + (i + 0)] += 2.0 * a->tag[i];
a->B[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tbd[i];
a->B[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tbg[i];
a->B[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tad[i];
a->E[(i + 0) * intp1 + (i + 0)] += 2.0 * a->tgg[i];
a->E[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tdd[i];
a->E[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tgd[i];
a->E[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tgd[i];
A[(i + 0) * intp1 + (i + 0)] += 2.0 * taa[i];
A[(i + 1) * intp1 + (i + 1)] = 2.0 * tbb[i];
A[(i + 0) * intp1 + (i + 1)] = 2.0 * tab[i];
A[(i + 1) * intp1 + (i + 0)] = 2.0 * tab[i];
B[(i + 0) * intp1 + (i + 0)] += 2.0 * tag[i];
B[(i + 1) * intp1 + (i + 1)] = 2.0 * tbd[i];
B[(i + 0) * intp1 + (i + 1)] = 2.0 * tbg[i];
B[(i + 1) * intp1 + (i + 0)] = 2.0 * tad[i];
E[(i + 0) * intp1 + (i + 0)] += 2.0 * tgg[i];
E[(i + 1) * intp1 + (i + 1)] = 2.0 * tdd[i];
E[(i + 0) * intp1 + (i + 1)] = 2.0 * tgd[i];
E[(i + 1) * intp1 + (i + 0)] = 2.0 * tgd[i];
}
for (i = 0; i < intm1; i++)
{
a->C[i * intp1 + (i + 0)] = +3.0 * a->h[i + 1] / a->h[i];
a->C[i * intp1 + (i + 2)] = -3.0 * a->h[i] / a->h[i + 1];
a->C[i * intp1 + (i + 1)] = -a->C[i * intp1 + (i + 0)] - a->C[i * intp1 + (i + 2)];
a->F[i * intp1 + (i + 0)] = a->h[i + 1];
a->F[i * intp1 + (i + 1)] = 2.0 * (a->h[i] + a->h[i + 1]);
a->F[i * intp1 + (i + 2)] = a->h[i];
C[i * intp1 + (i + 0)] = +3.0 * h[i + 1] / h[i];
C[i * intp1 + (i + 2)] = -3.0 * h[i] / h[i + 1];
C[i * intp1 + (i + 1)] = -C[i * intp1 + (i + 0)] - C[i * intp1 + (i + 2)];
F[i * intp1 + (i + 0)] = h[i + 1];
F[i * intp1 + (i + 1)] = 2.0 * (h[i] + h[i + 1]);
F[i * intp1 + (i + 2)] = h[i];
}
for (i = 0, k = 0; i < intp1; i++, k++)
{
for (j = 0, m = 0; j < intp1; j++, m++)
a->MAT[k * nsize + m] = a->A[i * intp1 + j];
MAT[k * nsize + m] = A[i * intp1 + j];
for (j = 0, m = intp1; j < intp1; j++, m++)
a->MAT[k * nsize + m] = a->B[j * intp1 + i];
MAT[k * nsize + m] = B[j * intp1 + i];
for (j = 0, m = 2 * intp1; j < intm1; j++, m++)
a->MAT[k * nsize + m] = a->C[j * intp1 + i];
a->RHS[k] = a->D[i];
MAT[k * nsize + m] = C[j * intp1 + i];
RHS[k] = D[i];
}
for (i = 0, k = intp1; i < intp1; i++, k++)
{
for (j = 0, m = 0; j < intp1; j++, m++)
a->MAT[k * nsize + m] = a->B[i * intp1 + j];
MAT[k * nsize + m] = B[i * intp1 + j];
for (j = 0, m = intp1; j < intp1; j++, m++)
a->MAT[k * nsize + m] = a->E[i * intp1 + j];
MAT[k * nsize + m] = E[i * intp1 + j];
for (j = 0, m = 2 * intp1; j < intm1; j++, m++)
a->MAT[k * nsize + m] = a->F[j * intp1 + i];
a->RHS[k] = a->G[i];
MAT[k * nsize + m] = F[j * intp1 + i];
RHS[k] = G[i];
}
for (i = 0, k = 2 * intp1; i < intm1; i++, k++)
{
for (j = 0, m = 0; j < intp1; j++, m++)
a->MAT[k * nsize + m] = a->C[i * intp1 + j];
MAT[k * nsize + m] = C[i * intp1 + j];
for (j = 0, m = intp1; j < intp1; j++, m++)
a->MAT[k * nsize + m] = a->F[i * intp1 + j];
MAT[k * nsize + m] = F[i * intp1 + j];
for (j = 0, m = 2 * intp1; j < intm1; j++, m++)
a->MAT[k * nsize + m] = 0.0;
a->RHS[k] = 0.0;
MAT[k * nsize + m] = 0.0;
RHS[k] = 0.0;
}
decomp(nsize, a->MAT, a->ipiv, &dinfo, a->wrk);
dsolve(nsize, a->MAT, a->ipiv, a->RHS, a->SLN);
decomp(nsize, MAT, ipiv, &dinfo, wrk);
dsolve(nsize, MAT, ipiv, RHS, SLN);
if (dinfo != 0)
{
*info = dinfo;
@ -387,15 +360,15 @@ void BLDR::xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t,
for (i = 0; i <= ints; i++)
{
a->z[i] = a->SLN[i];
a->zp[i] = a->SLN[i + ints + 1];
z[i] = SLN[i];
zp[i] = SLN[i + ints + 1];
}
for (i = 0; i < ints; i++)
{
c[4 * i + 0] = a->z[i];
c[4 * i + 1] = a->zp[i];
c[4 * i + 2] = -3.0 / (a->h[i] * a->h[i]) * (a->z[i] - a->z[i + 1]) - 1.0 / a->h[i] * (2.0 * a->zp[i] + a->zp[i + 1]);
c[4 * i + 3] = 2.0 / (a->h[i] * a->h[i] * a->h[i]) * (a->z[i] - a->z[i + 1]) + 1.0 / (a->h[i] * a->h[i]) * (a->zp[i] + a->zp[i + 1]);
c[4 * i + 0] = z[i];
c[4 * i + 1] = zp[i];
c[4 * i + 2] = -3.0 / (h[i] * h[i]) * (z[i] - z[i + 1]) - 1.0 / h[i] * (2.0 * zp[i] + zp[i + 1]);
c[4 * i + 3] = 2.0 / (h[i] * h[i] * h[i]) * (z[i] - z[i + 1]) + 1.0 / (h[i] * h[i]) * (zp[i] + zp[i + 1]);
}
cleanup:
return;

View File

@ -28,6 +28,8 @@ warren@wpratt.com
#ifndef wdsp_bldr_h
#define wdsp_bldr_h
#include <vector>
#include "export.h"
namespace WDSP {
@ -35,47 +37,50 @@ namespace WDSP {
class WDSP_API BLDR
{
public:
float* catxy;
float* sx;
float* sy;
float* h;
int* p;
int* np;
float* taa;
float* tab;
float* tag;
float* tad;
float* tbb;
float* tbg;
float* tbd;
float* tgg;
float* tgd;
float* tdd;
float* A;
float* B;
float* C;
float* D;
float* E;
float* F;
float* G;
float* MAT;
float* RHS;
float* SLN;
float* z;
float* zp;
float* wrk;
int* ipiv;
double* catxy;
std::vector<double> sx;
std::vector<double> sy;
std::vector<double> h;
std::vector<int> p;
std::vector<int> np;
std::vector<double> taa;
std::vector<double> tab;
std::vector<double> tag;
std::vector<double> tad;
std::vector<double> tbb;
std::vector<double> tbg;
std::vector<double> tbd;
std::vector<double> tgg;
std::vector<double> tgd;
std::vector<double> tdd;
std::vector<double> A;
std::vector<double> B;
std::vector<double> C;
std::vector<double> D;
std::vector<double> E;
std::vector<double> F;
std::vector<double> G;
std::vector<double> MAT;
std::vector<double> RHS;
std::vector<double> SLN;
std::vector<double> z;
std::vector<double> zp;
std::vector<double> wrk;
std::vector<int> ipiv;
static BLDR* create_builder(int points, int ints);
static void destroy_builder(BLDR *a);
static void flush_builder(BLDR *a, int points, int ints);
static void xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t, int* info, float* c, float ptol);
BLDR(int points, int ints);
BLDR(const BLDR&) = delete;
BLDR& operator=(const BLDR& other) = delete;
~BLDR();
void flush(int points);
void execute(int points, const double* x, const double* y, int ints, const double* t, int* info, double* c, double ptol);
private:
static int fcompare(const void* a, const void* b);
static void decomp(int n, float* a, int* piv, int* info, float* wrk);
static void dsolve(int n, float* a, int* piv, float* b, float* x);
static void cull(int* n, int ints, float* x, float* t, float ptol);
static void decomp(int n, std::vector<double>& a, std::vector<int>& piv, int* info, std::vector<double>& wrk);
static void dsolve(int n, std::vector<double>& a, std::vector<int>& piv, std::vector<double>& b, std::vector<double>& x);
static void cull(int* n, int ints, std::vector<double>& x, const double* t, double ptol);
};
} // namespace WDSP

View File

@ -40,106 +40,123 @@ namespace WDSP {
* *
********************************************************************************************************/
void BPS::calc_bps (BPS *a)
void BPS::calc()
{
float* impulse;
a->infilt = new float[2 * a->size * 2];
a->product = new float[2 * a->size * 2];
impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults(2 * a->size, impulse);
a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
delete[](impulse);
infilt.resize(2 * size * 2);
product.resize(2 * size * 2);
std::vector<float> impulse;
FIR::fir_bandpass(
impulse,
size + 1,
f_low,
f_high,
samplerate,
wintype,
1,
1.0 / (float)(2 * size)
);
FIR::fftcv_mults(mults, 2 * size, impulse.data());
CFor = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) infilt.data(), (fftwf_complex *) product.data(), FFTW_FORWARD, FFTW_PATIENT);
CRev = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) product.data(), (fftwf_complex *) out, FFTW_BACKWARD, FFTW_PATIENT);
}
void BPS::decalc_bps (BPS *a)
void BPS::decalc()
{
fftwf_destroy_plan(a->CRev);
fftwf_destroy_plan(a->CFor);
delete[] (a->mults);
delete[] (a->product);
delete[] (a->infilt);
fftwf_destroy_plan(CRev);
fftwf_destroy_plan(CFor);
}
BPS* BPS::create_bps (int run, int position, int size, float* in, float* out,
float f_low, float f_high, int samplerate, int wintype, float gain)
BPS::BPS(
int _run,
int _position,
int _size,
float* _in,
float* _out,
double _f_low,
double _f_high,
int _samplerate,
int _wintype,
double _gain
) :
run(_run),
position(_position),
size(_size),
in(_in),
out(_out),
f_low(_f_low),
f_high(_f_high),
samplerate((double) _samplerate),
wintype(_wintype),
gain(_gain)
{
BPS *a = new BPS;
a->run = run;
a->position = position;
a->size = size;
a->samplerate = (float)samplerate;
a->wintype = wintype;
a->gain = gain;
a->in = in;
a->out = out;
a->f_low = f_low;
a->f_high = f_high;
calc_bps (a);
return a;
calc();
}
void BPS::destroy_bps (BPS *a)
BPS::~BPS()
{
decalc_bps (a);
delete a;
decalc();
}
void BPS::flush_bps (BPS *a)
void BPS::flush()
{
std::fill(a->infilt, a->infilt + 2 * a->size * 2, 0);
std::fill(infilt.begin(), infilt.end(), 0);
}
void BPS::xbps (BPS *a, int pos)
void BPS::execute(int pos)
{
int i;
float I, Q;
if (a->run && pos == a->position)
double I;
double Q;
if (run && pos == position)
{
std::copy(a->in, a->in + a->size * 2, &(a->infilt[2 * a->size]));
fftwf_execute (a->CFor);
for (i = 0; i < 2 * a->size; i++)
std::copy(in, in + size * 2, &(infilt[2 * size]));
fftwf_execute (CFor);
for (int i = 0; i < 2 * size; i++)
{
I = a->gain * a->product[2 * i + 0];
Q = a->gain * a->product[2 * i + 1];
a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1];
a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0];
I = gain * product[2 * i + 0];
Q = gain * product[2 * i + 1];
product[2 * i + 0] = (float) (I * mults[2 * i + 0] - Q * mults[2 * i + 1]);
product[2 * i + 1] = (float) (I * mults[2 * i + 1] + Q * mults[2 * i + 0]);
}
fftwf_execute (a->CRev);
std::copy(&(a->infilt[2 * a->size]), &(a->infilt[2 * a->size]) + a->size * 2, a->infilt);
fftwf_execute (CRev);
std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt.begin());
}
else if (a->in != a->out)
std::copy( a->in, a->in + a->size * 2, a->out);
else if (in != out)
std::copy( in, in + size * 2, out);
}
void BPS::setBuffers_bps (BPS *a, float* in, float* out)
void BPS::setBuffers(float* _in, float* _out)
{
decalc_bps (a);
a->in = in;
a->out = out;
calc_bps (a);
decalc();
in = _in;
out = _out;
calc();
}
void BPS::setSamplerate_bps (BPS *a, int rate)
void BPS::setSamplerate(int rate)
{
decalc_bps (a);
a->samplerate = rate;
calc_bps (a);
decalc();
samplerate = rate;
calc();
}
void BPS::setSize_bps (BPS *a, int size)
void BPS::setSize(int _size)
{
decalc_bps (a);
a->size = size;
calc_bps (a);
decalc();
size = _size;
calc();
}
void BPS::setFreqs_bps (BPS *a, float f_low, float f_high)
void BPS::setFreqs(double _f_low, double _f_high)
{
decalc_bps (a);
a->f_low = f_low;
a->f_high = f_high;
calc_bps (a);
decalc();
f_low = _f_low;
f_high = _f_high;
calc();
}
void BPS::setRun(int _run)
{
run = _run;
}
/********************************************************************************************************
@ -148,132 +165,4 @@ void BPS::setFreqs_bps (BPS *a, float f_low, float f_high)
* *
********************************************************************************************************/
void BPS::SetBPSRun (RXA& rxa, int run)
{
rxa.bp1.p->run = run;
}
void BPS::SetBPSFreqs (RXA& rxa, float f_low, float f_high)
{
float* impulse;
BPS *a1;
a1 = rxa.bps1.p;
if ((f_low != a1->f_low) || (f_high != a1->f_high))
{
a1->f_low = f_low;
a1->f_high = f_high;
delete[] (a1->mults);
impulse = FIR::fir_bandpass(a1->size + 1, f_low, f_high, a1->samplerate, a1->wintype, 1, 1.0 / (float)(2 * a1->size));
a1->mults = FIR::fftcv_mults (2 * a1->size, impulse);
delete[] (impulse);
}
}
void BPS::SetBPSWindow (RXA& rxa, int wintype)
{
float* impulse;
BPS *a1;
a1 = rxa.bps1.p;
if ((a1->wintype != wintype))
{
a1->wintype = wintype;
delete[] (a1->mults);
impulse = FIR::fir_bandpass(a1->size + 1, a1->f_low, a1->f_high, a1->samplerate, a1->wintype, 1, 1.0 / (float)(2 * a1->size));
a1->mults = FIR::fftcv_mults (2 * a1->size, impulse);
delete[] (impulse);
}
}
/********************************************************************************************************
* *
* TXA Properties *
* *
********************************************************************************************************/
// UNCOMMENT properties when pointers in place in txa
void BPS::SetBPSRun (TXA& txa, int run)
{
txa.bp1.p->run = run;
}
void BPS::SetBPSFreqs (TXA& txa, float f_low, float f_high)
{
float* impulse;
BPS *a;
a = txa.bps0.p;
if ((f_low != a->f_low) || (f_high != a->f_high))
{
a->f_low = f_low;
a->f_high = f_high;
delete[] (a->mults);
impulse = FIR::fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
delete[] (impulse);
}
a = txa.bps1.p;
if ((f_low != a->f_low) || (f_high != a->f_high))
{
a->f_low = f_low;
a->f_high = f_high;
delete[] (a->mults);
impulse = FIR::fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
delete[] (impulse);
}
a = txa.bps2.p;
if ((f_low != a->f_low) || (f_high != a->f_high))
{
a->f_low = f_low;
a->f_high = f_high;
delete[] (a->mults);
impulse = FIR::fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
delete[] (impulse);
}
}
void BPS::SetBPSWindow (TXA& txa, int wintype)
{
float* impulse;
BPS *a;
a = txa.bps0.p;
if (a->wintype != wintype)
{
a->wintype = wintype;
delete[] (a->mults);
impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
delete[] (impulse);
}
a = txa.bps1.p;
if (a->wintype != wintype)
{
a->wintype = wintype;
delete[] (a->mults);
impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
delete[] (impulse);
}
a = txa.bps2.p;
if (a->wintype != wintype)
{
a->wintype = wintype;
delete[] (a->mults);
impulse = FIR::fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size));
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
delete[] (impulse);
}
}
} // namespace WDSP

View File

@ -34,6 +34,8 @@ warren@wpratt.com
#ifndef wdsp_bps_h
#define wdsp_bps_h
#include <vector>
#include "fftw3.h"
#include "export.h"
@ -50,38 +52,44 @@ public:
int size;
float* in;
float* out;
float f_low;
float f_high;
float* infilt;
float* product;
float* mults;
float samplerate;
double f_low;
double f_high;
std::vector<float> infilt;
std::vector<float> product;
std::vector<float> mults;
double samplerate;
int wintype;
float gain;
double gain;
fftwf_plan CFor;
fftwf_plan CRev;
static BPS* create_bps (int run, int position, int size, float* in, float* out,
float f_low, float f_high, int samplerate, int wintype, float gain);
static void destroy_bps (BPS *a);
static void flush_bps (BPS *a);
static void xbps (BPS *a, int pos);
static void setBuffers_bps (BPS *a, float* in, float* out);
static void setSamplerate_bps (BPS *a, int rate);
static void setSize_bps (BPS *a, int size);
static void setFreqs_bps (BPS *a, float f_low, float f_high);
// RXA Prototypes
static void SetBPSRun (RXA& rxa, int run);
static void SetBPSFreqs (RXA& rxa, float low, float high);
static void SetBPSWindow (RXA& rxa, int wintype);
// TXA Prototypes
static void SetBPSRun (TXA& txa, int run);
static void SetBPSFreqs (TXA& txa, float low, float high);
static void SetBPSWindow (TXA& txa, int wintype);
BPS(
int run,
int position,
int size,
float* in,
float* out,
double f_low,
double f_high,
int samplerate,
int wintype,
double gain
);
BPS(const BPS&) = delete;
BPS& operator=(const BPS& other) = delete;
~BPS();
void flush();
void execute(int pos);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
void setFreqs(double f_low, double f_high);
void setRun(int run);
private:
static void calc_bps (BPS *a);
static void decalc_bps (BPS *a);
void calc();
void decalc();
};
} // namespace WDSP

View File

@ -35,7 +35,6 @@ warren@wpratt.com
#include "anr.hpp"
#include "emnr.hpp"
#include "bpsnba.hpp"
#include "RXA.hpp"
#define MAXIMP 256
@ -51,167 +50,158 @@ namespace WDSP {
// for its input and output to happen at different points in the processing pipeline. This means it must
// include a buffer, 'buff'. Its input and output are done via functions xbpshbain() and xbpshbaout().
void BPSNBA::calc_bpsnba (BPSNBA *a)
void BPSNBA::calc()
{
a->buff = new float[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex));
a->bpsnba = NBP::create_nbp (
buff.resize(size * 2);
bpsnba = new NBP (
1, // run, always runs (use bpsnba 'run')
a->run_notches, // run the notches
run_notches, // run the notches
0, // position variable for nbp (not for bpsnba), always 0
a->size, // buffer size
a->nc, // number of filter coefficients
a->mp, // minimum phase flag
a->buff, // pointer to input buffer
a->out, // pointer to output buffer
a->f_low, // lower filter frequency
a->f_high, // upper filter frequency
a->rate, // sample rate
a->wintype, // wintype
a->gain, // gain
a->autoincr, // auto-increase notch width if below min
a->maxpb, // max number of passbands
a->ptraddr); // addr of database pointer
size, // buffer size
nc, // number of filter coefficients
mp, // minimum phase flag
buff.data(), // pointer to input buffer
out, // pointer to output buffer
f_low, // lower filter frequency
f_high, // upper filter frequency
rate, // sample rate
wintype, // wintype
gain, // gain
autoincr, // auto-increase notch width if below min
maxpb, // max number of passbands
notchdb); // addr of database pointer
}
BPSNBA* BPSNBA::create_bpsnba (
int run,
int run_notches,
int position,
int size,
int nc,
int mp,
float* in,
float* out,
int rate,
double abs_low_freq,
double abs_high_freq,
double f_low,
double f_high,
int wintype,
double gain,
int autoincr,
int maxpb,
NOTCHDB* ptraddr
)
BPSNBA::BPSNBA(
int _run,
int _run_notches,
int _position,
int _size,
int _nc,
int _mp,
float* _in,
float* _out,
int _rate,
double _abs_low_freq,
double _abs_high_freq,
double _f_low,
double _f_high,
int _wintype,
double _gain,
int _autoincr,
int _maxpb,
NOTCHDB* _notchdb
) :
run(_run),
run_notches(_run_notches),
position(_position),
size(_size),
nc(_nc),
mp(_mp),
in(_in),
out(_out),
rate(_rate),
abs_low_freq(_abs_low_freq),
abs_high_freq(_abs_high_freq),
f_low(_f_low),
f_high(_f_high),
wintype(_wintype),
gain(_gain),
autoincr(_autoincr),
maxpb(_maxpb),
notchdb(_notchdb)
{
BPSNBA *a = new BPSNBA;
a->run = run;
a->run_notches = run_notches;
a->position = position;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->rate = rate;
a->abs_low_freq = abs_low_freq;
a->abs_high_freq = abs_high_freq;
a->f_low = f_low;
a->f_high = f_high;
a->wintype = wintype;
a->gain = gain;
a->autoincr = autoincr;
a->maxpb = maxpb;
a->ptraddr = ptraddr;
calc_bpsnba (a);
return a;
calc();
}
void BPSNBA::decalc_bpsnba (BPSNBA *a)
void BPSNBA::decalc()
{
NBP::destroy_nbp (a->bpsnba);
delete[] (a->buff);
delete bpsnba;
}
void BPSNBA::destroy_bpsnba (BPSNBA *a)
BPSNBA::~BPSNBA()
{
decalc_bpsnba (a);
delete[] (a);
decalc();
}
void BPSNBA::flush_bpsnba (BPSNBA *a)
void BPSNBA::flush()
{
std::fill(a->buff, a->buff + a->size * 2, 0);
NBP::flush_nbp (a->bpsnba);
std::fill(buff.begin(), buff.end(), 0);
bpsnba->flush();
}
void BPSNBA::setBuffers_bpsnba (BPSNBA *a, float* in, float* out)
void BPSNBA::setBuffers(float* _in, float* _out)
{
decalc_bpsnba (a);
a->in = in;
a->out = out;
calc_bpsnba (a);
decalc();
in = _in;
out = _out;
calc();
}
void BPSNBA::setSamplerate_bpsnba (BPSNBA *a, int rate)
void BPSNBA::setSamplerate(int _rate)
{
decalc_bpsnba (a);
a->rate = rate;
calc_bpsnba (a);
decalc();
rate = _rate;
calc();
}
void BPSNBA::setSize_bpsnba (BPSNBA *a, int size)
void BPSNBA::setSize(int _size)
{
decalc_bpsnba (a);
a->size = size;
calc_bpsnba (a);
decalc();
size = _size;
calc();
}
void BPSNBA::xbpsnbain (BPSNBA *a, int position)
void BPSNBA::exec_in(int _position)
{
if (a->run && a->position == position)
std::copy(a->in, a->in + a->size * 2, a->buff);
if (run && position == _position)
std::copy(in, in + size * 2, buff.begin());
}
void BPSNBA::xbpsnbaout (BPSNBA *a, int position)
void BPSNBA::exec_out(int _position)
{
if (a->run && a->position == position)
NBP::xnbp (a->bpsnba, 0);
if (run && position == _position)
bpsnba->execute(0);
}
void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, int update)
void BPSNBA::recalc_bpsnba_filter(int update)
{
// Call anytime one of the parameters listed below has been changed in
// the BPSNBA struct.
NBP *b = a->bpsnba;
b->fnfrun = a->run_notches;
b->flow = a->f_low;
b->fhigh = a->f_high;
b->wintype = a->wintype;
b->gain = a->gain;
b->autoincr = a->autoincr;
NBP::calc_nbp_impulse (b);
FIRCORE::setImpulse_fircore (b->p, b->impulse, update);
delete[] (b->impulse);
NBP *b = bpsnba;
b->fnfrun = run_notches;
b->flow = f_low;
b->fhigh = f_high;
b->wintype = wintype;
b->gain = gain;
b->autoincr = autoincr;
b->calc_impulse();
b->fircore->setImpulse(b->impulse, update);
}
/********************************************************************************************************
* *
* RXA Properties *
* Properties *
* *
********************************************************************************************************/
void BPSNBA::BPSNBASetNC (RXA& rxa, int nc)
void BPSNBA::SetNC(int _nc)
{
BPSNBA *a = rxa.bpsnba.p;
if (a->nc != nc)
if (nc != _nc)
{
a->nc = nc;
a->bpsnba->nc = a->nc;
NBP::setNc_nbp (a->bpsnba);
nc = _nc;
bpsnba->nc = nc;
bpsnba->setNc();
}
}
void BPSNBA::BPSNBASetMP (RXA& rxa, int mp)
void BPSNBA::SetMP(int _mp)
{
BPSNBA *a = rxa.bpsnba.p;
if (a->mp != mp)
if (mp != _mp)
{
a->mp = mp;
a->bpsnba->mp = a->mp;
NBP::setMp_nbp (a->bpsnba);
mp = _mp;
bpsnba->mp = mp;
bpsnba->setMp();
}
}

View File

@ -28,14 +28,15 @@ warren@wpratt.com
#ifndef wdsp_bpsnba_h
#define wdsp_bpsnba_h
namespace WDSP{
#include <vector>
class RXA;
#include "export.h"
namespace WDSP{
class NOTCHDB;
class NBP;
class BPSNBA
class WDSP_API BPSNBA
{
public:
int run; // run the filter
@ -47,19 +48,19 @@ public:
float* in; // input buffer
float* out; // output buffer
int rate; // sample rate
float* buff; // internal buffer
NBP *bpsnba; // pointer to the notched bandpass filter, nbp
double f_low; // low cutoff frequency
double f_high; // high cutoff frequency
double abs_low_freq; // lowest positive freq supported by SNB
double abs_high_freq; // highest positive freq supported by SNG
double f_low; // low cutoff frequency
double f_high; // high cutoff frequency
std::vector<float> buff; // internal buffer
int wintype; // filter window type
double gain; // filter gain
int autoincr; // use auto increment for notch width
int maxpb; // maximum passband segments supported
NOTCHDB* ptraddr; // pointer to address of NOTCH DATABASE
NOTCHDB* notchdb; // pointer to address of NOTCH DATABASE
NBP *bpsnba; // pointer to the notched bandpass filter, nbp
static BPSNBA* create_bpsnba (
BPSNBA(
int run,
int run_notches,
int position,
@ -77,23 +78,26 @@ public:
double gain,
int autoincr,
int maxpb,
NOTCHDB* ptraddr
NOTCHDB* notchdb
);
static void destroy_bpsnba (BPSNBA *a);
static void flush_bpsnba (BPSNBA *a);
static void setBuffers_bpsnba (BPSNBA *a, float* in, float* out);
static void setSamplerate_bpsnba (BPSNBA *a, int rate);
static void setSize_bpsnba (BPSNBA *a, int size);
static void xbpsnbain (BPSNBA *a, int position);
static void xbpsnbaout (BPSNBA *a, int position);
static void recalc_bpsnba_filter (BPSNBA *a, int update);
// RXA Propertoes
static void BPSNBASetNC (RXA& rxa, int nc);
static void BPSNBASetMP (RXA& rxa, int mp);
BPSNBA(const BPSNBA&) = delete;
BPSNBA& operator=(BPSNBA& other) = delete;
~BPSNBA();
void flush();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
void exec_in(int position);
void exec_out(int position);
void recalc_bpsnba_filter(int update);
// Propertoes
void SetNC(int nc);
void SetMP(int mp);
private:
static void calc_bpsnba (BPSNBA *a);
static void decalc_bpsnba (BPSNBA *a);
void calc();
void decalc();
};

159
wdsp/bqbp.cpp Normal file
View File

@ -0,0 +1,159 @@
/* iir.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "bqbp.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Complex Bi-Quad Band-Pass *
* *
********************************************************************************************************/
void BQBP::calc()
{
double f0;
double w0;
double bw;
double q;
double sn;
double cs;
double c;
double den;
bw = f_high - f_low;
f0 = (f_high + f_low) / 2.0;
q = f0 / bw;
w0 = TWOPI * f0 / rate;
sn = sin(w0);
cs = cos(w0);
c = sn / (2.0 * q);
den = 1.0 + c;
a0 = +c / den;
a1 = 0.0;
a2 = -c / den;
b1 = 2.0 * cs / den;
b2 = (c - 1.0) / den;
flush();
}
BQBP::BQBP(
int _run,
int _size,
float* _in,
float* _out,
double _rate,
double _f_low,
double _f_high,
double _gain,
int _nstages
) :
run(_run),
size(_size),
in(_in),
out(_out),
rate(_rate),
f_low(_f_low),
f_high(_f_high),
gain(_gain),
nstages(_nstages)
{
x0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
x1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
x2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
y0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
y1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
y2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
calc();
}
void BQBP::flush()
{
for (int i = 0; i < nstages; i++)
{
x1[2 * i + 0] = x2[2 * i + 0] = y1[2 * i + 0] = y2[2 * i + 0] = 0.0;
x1[2 * i + 1] = x2[2 * i + 1] = y1[2 * i + 1] = y2[2 * i + 1] = 0.0;
}
}
void BQBP::execute()
{
if (run)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < 2; j++)
{
x0[j] = gain * in[2 * i + j];
for (int n = 0; n < nstages; n++)
{
if (n > 0)
x0[2 * n + j] = y0[2 * (n - 1) + j];
y0[2 * n + j] = a0 * x0[2 * n + j]
+ a1 * x1[2 * n + j]
+ a2 * x2[2 * n + j]
+ b1 * y1[2 * n + j]
+ b2 * y2[2 * n + j];
y2[2 * n + j] = y1[2 * n + j];
y1[2 * n + j] = y0[2 * n + j];
x2[2 * n + j] = x1[2 * n + j];
x1[2 * n + j] = x0[2 * n + j];
}
out[2 * i + j] = (float) y0[2 * (nstages - 1) + j];
}
}
}
else if (out != in)
{
std::copy(in, in + size * 2, out);
}
}
void BQBP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
}
void BQBP::setSamplerate( int _rate)
{
rate = _rate;
calc();
}
void BQBP::setSize(int _size)
{
size = _size;
flush();
}
} // namespace WDSP

85
wdsp/bqbp.hpp Normal file
View File

@ -0,0 +1,85 @@
/* bqbp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Complex Bi-Quad Band-Pass *
* *
********************************************************************************************************/
#ifndef wdsp_bqbp_h
#define wdsp_bqbp_h
#include <vector>
#include "export.h"
namespace WDSP {
class WDSP_API BQBP
{
public:
int run;
int size;
float* in;
float* out;
double rate;
double f_low;
double f_high;
double gain;
int nstages;
double a0, a1, a2, b1, b2;
std::vector<double> x0, x1, x2, y0, y1, y2;
BQBP(
int run,
int size,
float* in,
float* out,
double rate,
double f_low,
double f_high,
double gain,
int nstages
);
BQBP(const BQBP&) = delete;
BQBP& operator=(BQBP& other) = delete;
~BQBP() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
void calc();
};
} // namespace WDSP
#endif

151
wdsp/bqlp.cpp Normal file
View File

@ -0,0 +1,151 @@
/* iir.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "bqlp.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Complex Bi-Quad Low-Pass *
* *
********************************************************************************************************/
void BQLP::calc()
{
double w0;
double cs;
double c;
double den;
w0 = TWOPI * fc / rate;
cs = cos(w0);
c = sin(w0) / (2.0 * Q);
den = 1.0 + c;
a0 = 0.5 * (1.0 - cs) / den;
a1 = (1.0 - cs) / den;
a2 = 0.5 * (1.0 - cs) / den;
b1 = 2.0 * cs / den;
b2 = (c - 1.0) / den;
flush();
}
BQLP::BQLP(
int _run,
int _size,
float* _in,
float* _out,
double _rate,
double _fc,
double _Q,
double _gain,
int _nstages
) :
run(_run),
size(_size),
in(_in),
out(_out),
rate(_rate),
fc(_fc),
Q(_Q),
gain(_gain),
nstages(_nstages)
{
x0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
x1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
x2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
y0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
y1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
y2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex));
calc();
}
void BQLP::flush()
{
for (int i = 0; i < nstages; i++)
{
x1[2 * i + 0] = x2[2 * i + 0] = y1[2 * i + 0] = y2[2 * i + 0] = 0.0;
x1[2 * i + 1] = x2[2 * i + 1] = y1[2 * i + 1] = y2[2 * i + 1] = 0.0;
}
}
void BQLP::execute()
{
if (run)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < 2; j++)
{
x0[j] = gain * in[2 * i + j];
for (int n = 0; n < nstages; n++)
{
if (n > 0)
x0[2 * n + j] = y0[2 * (n - 1) + j];
y0[2 * n + j] = a0 * x0[2 * n + j]
+ a1 * x1[2 * n + j]
+ a2 * x2[2 * n + j]
+ b1 * y1[2 * n + j]
+ b2 * y2[2 * n + j];
y2[2 * n + j] = y1[2 * n + j];
y1[2 * n + j] = y0[2 * n + j];
x2[2 * n + j] = x1[2 * n + j];
x1[2 * n + j] = x0[2 * n + j];
}
out[2 * i + j] = (float) y0[2 * (nstages - 1) + j];
}
}
}
else if (out != in)
{
std::copy(in, in + size * 2, out);
}
}
void BQLP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
}
void BQLP::setSamplerate(int _rate)
{
rate = _rate;
calc();
}
void BQLP::setSize(int _size)
{
size = _size;
flush();
}
} // namespace WDSP

94
wdsp/bqlp.hpp Normal file
View File

@ -0,0 +1,94 @@
/* bqlp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Complex Bi-Quad Low-Pass *
* *
********************************************************************************************************/
#ifndef wdsp_bqlp_h
#define wdsp_bqlp_h
#include <vector>
#include "export.h"
namespace WDSP {
class WDSP_API BQLP
{
public:
int run;
int size;
float* in;
float* out;
double rate;
double fc;
double Q;
double gain;
int nstages;
double a0;
double a1;
double a2;
double b1;
double b2;
std::vector<double> x0;
std::vector<double> x1;
std::vector<double> x2;
std::vector<double> y0;
std::vector<double> y1;
std::vector<double> y2;
BQLP(
int run,
int size,
float* in,
float* out,
double rate,
double fc,
double Q,
double gain,
int nstages
);
BQLP(const BQLP&) = delete;
BQLP& operator=(BQLP& other) = delete;
~BQLP() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
void calc();
};
} // namespace WDSP
#endif

View File

@ -29,7 +29,7 @@ warren@wpratt.com
namespace WDSP {
const double Calculus::GG[241 * 241] = {
const std::array<double, 241*241> Calculus::GG = {
7.25654181154076983e-01, 7.05038822098223439e-01, 6.85008217584843870e-01, 6.65545775927326222e-01,
6.46635376294157682e-01, 6.28261355371665386e-01, 6.10408494407843394e-01, 5.93062006626410732e-01,
5.76207525000389742e-01, 5.59831090374464435e-01, 5.43919139925240769e-01, 5.28458495948192608e-01,
@ -14552,7 +14552,7 @@ const double Calculus::GG[241 * 241] = {
1.00000000000000000e+00, 1.00000000000000000e+00, 1.00000000000000000e+00, 1.00000000000000000e+00,
1.00000000000000000e+00 };
const double Calculus::GGS[241 * 241] = {
const std::array<double, 241*241> Calculus::GGS = {
8.00014908335353492e-01, 8.00020707540703313e-01, 8.00026700706648830e-01, 8.00032894400760863e-01,
8.00039295417528384e-01, 8.00045910786425396e-01, 8.00052747780268358e-01, 8.00059813923879481e-01,
8.00067117003061101e-01, 8.00074665073896907e-01, 8.00082466472385456e-01, 8.00090529824419749e-01,

View File

@ -29,6 +29,8 @@ warren@wpratt.com
#ifndef wdsp_calculus_h
#define wdsp_calculus_h
#include <array>
#include "export.h"
namespace WDSP {
@ -36,8 +38,8 @@ namespace WDSP {
class WDSP_API Calculus
{
public:
static const double GG[];
static const double GGS[];
static const std::array<double, 241*241> GG;
static const std::array<double, 241*241> GGS;
};
} // namespace WDSP

View File

@ -27,99 +27,93 @@ warren@wpratt.com
#include "comm.hpp"
#include "cblock.hpp"
#include "RXA.hpp"
namespace WDSP {
void CBL::calc_cbl (CBL *a)
void CBL::calc()
{
a->prevIin = 0.0;
a->prevQin = 0.0;
a->prevIout = 0.0;
a->prevQout = 0.0;
a->mtau = exp(-1.0 / (a->sample_rate * a->tau));
prevIin = 0.0;
prevQin = 0.0;
prevIout = 0.0;
prevQout = 0.0;
mtau = exp(-1.0 / (sample_rate * tau));
}
CBL* CBL::create_cbl(
int run,
int buff_size,
float *in_buff,
float *out_buff,
int mode,
int sample_rate,
double tau
)
CBL::CBL(
int _run,
int _buff_size,
float *_in_buff,
float *_out_buff,
int _mode,
int _sample_rate,
double _tau
) :
run(_run),
buff_size(_buff_size),
in_buff(_in_buff),
out_buff(_out_buff),
mode(_mode),
sample_rate((double) _sample_rate),
tau(_tau)
{
CBL *a = new CBL;
a->run = run;
a->buff_size = buff_size;
a->in_buff = in_buff;
a->out_buff = out_buff;
a->mode = mode;
a->sample_rate = (double) sample_rate;
a->tau = tau;
calc_cbl (a);
return a;
calc();
}
void CBL::destroy_cbl(CBL *a)
void CBL::flush()
{
delete a;
prevIin = 0.0;
prevQin = 0.0;
prevIout = 0.0;
prevQout = 0.0;
}
void CBL::flush_cbl (CBL *a)
void CBL::execute()
{
a->prevIin = 0.0;
a->prevQin = 0.0;
a->prevIout = 0.0;
a->prevQout = 0.0;
}
void CBL::xcbl (CBL *a)
{
if (a->run)
if (run)
{
int i;
double tempI, tempQ;
double tempI;
double tempQ;
for (i = 0; i < a->buff_size; i++)
for (int i = 0; i < buff_size; i++)
{
tempI = a->in_buff[2 * i + 0];
tempQ = a->in_buff[2 * i + 1];
a->out_buff[2 * i + 0] = a->in_buff[2 * i + 0] - a->prevIin + a->mtau * a->prevIout;
a->out_buff[2 * i + 1] = a->in_buff[2 * i + 1] - a->prevQin + a->mtau * a->prevQout;
a->prevIin = tempI;
a->prevQin = tempQ;
tempI = in_buff[2 * i + 0];
tempQ = in_buff[2 * i + 1];
out_buff[2 * i + 0] = (float) (in_buff[2 * i + 0] - prevIin + mtau * prevIout);
out_buff[2 * i + 1] = (float) (in_buff[2 * i + 1] - prevQin + mtau * prevQout);
prevIin = tempI;
prevQin = tempQ;
prevIout = out_buff[2 * i + 0];
prevQout = out_buff[2 * i + 1];
if (fabs(a->prevIout = a->out_buff[2 * i + 0]) < 1.0e-20)
a->prevIout = 0.0;
if (fabs(prevIout) < 1.0e-20)
prevIout = 0.0;
if (fabs(a->prevQout = a->out_buff[2 * i + 1]) < 1.0e-20)
a->prevQout = 0.0;
if (fabs(prevQout) < 1.0e-20)
prevQout = 0.0;
}
}
else if (a->in_buff != a->out_buff)
else if (in_buff != out_buff)
{
std::copy(a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff);
std::copy(in_buff, in_buff + buff_size * 2, out_buff);
}
}
void CBL::setBuffers_cbl (CBL *a, float* in, float* out)
void CBL::setBuffers(float* _in, float* _out)
{
a->in_buff = in;
a->out_buff = out;
in_buff = _in;
out_buff = _out;
}
void CBL::setSamplerate_cbl (CBL *a, int rate)
void CBL::setSamplerate(int _rate)
{
a->sample_rate = rate;
calc_cbl (a);
sample_rate = _rate;
calc();
}
void CBL::setSize_cbl (CBL *a, int size)
void CBL::setSize(int _size)
{
a->buff_size = size;
flush_cbl (a);
buff_size = _size;
flush();
}
/********************************************************************************************************
@ -128,9 +122,9 @@ void CBL::setSize_cbl (CBL *a, int size)
* *
********************************************************************************************************/
void CBL::SetCBLRun(RXA& rxa, int setit)
void CBL::setRun(int setit)
{
rxa.cbl.p->run = setit;
run = setit;
}
} // namespace WDSP

View File

@ -32,8 +32,6 @@ warren@wpratt.com
namespace WDSP {
class RXA;
class WDSP_API CBL
{
public:
@ -50,7 +48,7 @@ public:
double tau; //carrier removal time constant
double mtau; //carrier removal multiplier
static CBL* create_cbl(
CBL(
int run,
int buff_size,
float *in_buff,
@ -59,17 +57,20 @@ public:
int sample_rate,
double tau
);
static void destroy_cbl (CBL *a);
static void flush_cbl (CBL *a);
static void xcbl (CBL *a);
static void setBuffers_cbl (CBL *a, float* in, float* out);
static void setSamplerate_cbl (CBL *a, int rate);
static void setSize_cbl (CBL *a, int size);
// RXA Properties
static void SetCBLRun(RXA& rxa, int setit);
CBL(const CBL&) = delete;
CBL& operator=(CBL& other) = delete;
~CBL() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// Public Properties
void setRun(int setit);
private:
static void calc_cbl (CBL *a);
void calc();
};
} // namespace WDSP

View File

@ -32,381 +32,384 @@ warren@wpratt.com
namespace WDSP {
void CFCOMP::calc_cfcwindow (CFCOMP *a)
void CFCOMP::calc_cfcwindow()
{
int i;
float arg0, arg1, cgsum, igsum, coherent_gain, inherent_power_gain, wmult;
switch (a->wintype)
double arg0;
double arg1;
double cgsum;
double igsum;
double coherent_gain;
double inherent_power_gain;
double wmult;
switch (wintype)
{
case 0:
arg0 = 2.0 * PI / (float)a->fsize;
arg0 = 2.0 * PI / (float)fsize;
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < a->fsize; i++)
for (i = 0; i < fsize; i++)
{
a->window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg0));
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg0));
cgsum += window[i];
igsum += window[i] * window[i];
}
coherent_gain = cgsum / (float)a->fsize;
inherent_power_gain = igsum / (float)a->fsize;
coherent_gain = cgsum / (float)fsize;
inherent_power_gain = igsum / (float)fsize;
wmult = 1.0 / sqrt (inherent_power_gain);
for (i = 0; i < a->fsize; i++)
a->window[i] *= wmult;
a->winfudge = sqrt (1.0 / coherent_gain);
for (i = 0; i < fsize; i++)
window[i] *= wmult;
winfudge = sqrt (1.0 / coherent_gain);
break;
case 1:
arg0 = 2.0 * PI / (float)a->fsize;
arg0 = 2.0 * PI / (float)fsize;
cgsum = 0.0;
igsum = 0.0;
for (i = 0; i < a->fsize; i++)
for (i = 0; i < fsize; i++)
{
arg1 = cos(arg0 * (float)i);
a->window[i] = sqrt (+0.21747
window[i] = sqrt (+0.21747
+ arg1 * (-0.45325
+ arg1 * (+0.28256
+ arg1 * (-0.04672))));
cgsum += a->window[i];
igsum += a->window[i] * a->window[i];
cgsum += window[i];
igsum += window[i] * window[i];
}
coherent_gain = cgsum / (float)a->fsize;
inherent_power_gain = igsum / (float)a->fsize;
coherent_gain = cgsum / (float)fsize;
inherent_power_gain = igsum / (float)fsize;
wmult = 1.0 / sqrt (inherent_power_gain);
for (i = 0; i < a->fsize; i++)
a->window[i] *= wmult;
a->winfudge = sqrt (1.0 / coherent_gain);
for (i = 0; i < fsize; i++)
window[i] *= wmult;
winfudge = sqrt (1.0 / coherent_gain);
break;
default:
break;
}
}
int CFCOMP::fCOMPcompare (const void *a, const void *b)
{
if (*(float*)a < *(float*)b)
if (*(double*)a < *(double*)b)
return -1;
else if (*(float*)a == *(float*)b)
else if (*(double*)a == *(double*)b)
return 0;
else
return 1;
}
void CFCOMP::calc_comp (CFCOMP *a)
void CFCOMP::calc_comp()
{
int i, j;
float f, frac, fincr, fmax;
float* sary;
a->precomplin = pow (10.0, 0.05 * a->precomp);
a->prepeqlin = pow (10.0, 0.05 * a->prepeq);
fmax = 0.5 * a->rate;
for (i = 0; i < a->nfreqs; i++)
int i;
int j;
double f;
double frac;
double fincr;
double fmax;
double* sary;
precomplin = pow (10.0, 0.05 * precomp);
prepeqlin = pow (10.0, 0.05 * prepeq);
fmax = 0.5 * rate;
for (i = 0; i < nfreqs; i++)
{
a->F[i] = std::max (a->F[i], 0.0f);
a->F[i] = std::min (a->F[i], fmax);
a->G[i] = std::max (a->G[i], 0.0f);
F[i] = std::max (F[i], 0.0);
F[i] = std::min (F[i], fmax);
G[i] = std::max (G[i], 0.0);
}
sary = new float[3 * a->nfreqs]; // (float *)malloc0 (3 * a->nfreqs * sizeof (float));
for (i = 0; i < a->nfreqs; i++)
sary = new double[3 * nfreqs];
for (i = 0; i < nfreqs; i++)
{
sary[3 * i + 0] = a->F[i];
sary[3 * i + 1] = a->G[i];
sary[3 * i + 2] = a->E[i];
sary[3 * i + 0] = F[i];
sary[3 * i + 1] = G[i];
sary[3 * i + 2] = E[i];
}
qsort (sary, a->nfreqs, 3 * sizeof (float), fCOMPcompare);
for (i = 0; i < a->nfreqs; i++)
qsort (sary, nfreqs, 3 * sizeof (float), fCOMPcompare);
for (i = 0; i < nfreqs; i++)
{
a->F[i] = sary[3 * i + 0];
a->G[i] = sary[3 * i + 1];
a->E[i] = sary[3 * i + 2];
F[i] = sary[3 * i + 0];
G[i] = sary[3 * i + 1];
E[i] = sary[3 * i + 2];
}
a->fp[0] = 0.0;
a->fp[a->nfreqs + 1] = fmax;
a->gp[0] = a->G[0];
a->gp[a->nfreqs + 1] = a->G[a->nfreqs - 1];
a->ep[0] = a->E[0]; // cutoff?
a->ep[a->nfreqs + 1] = a->E[a->nfreqs - 1]; // cutoff?
for (i = 0, j = 1; i < a->nfreqs; i++, j++)
fp[0] = 0.0;
fp[nfreqs + 1] = fmax;
gp[0] = G[0];
gp[nfreqs + 1] = G[nfreqs - 1];
ep[0] = E[0]; // cutoff?
ep[nfreqs + 1] = E[nfreqs - 1]; // cutoff?
for (i = 0, j = 1; i < nfreqs; i++, j++)
{
a->fp[j] = a->F[i];
a->gp[j] = a->G[i];
a->ep[j] = a->E[i];
fp[j] = F[i];
gp[j] = G[i];
ep[j] = E[i];
}
fincr = a->rate / (float)a->fsize;
fincr = rate / (float)fsize;
j = 0;
// print_impulse ("gp.txt", a->nfreqs+2, a->gp, 0, 0);
for (i = 0; i < a->msize; i++)
for (i = 0; i < msize; i++)
{
f = fincr * (float)i;
while (f >= a->fp[j + 1] && j < a->nfreqs) j++;
frac = (f - a->fp[j]) / (a->fp[j + 1] - a->fp[j]);
a->comp[i] = pow (10.0, 0.05 * (frac * a->gp[j + 1] + (1.0 - frac) * a->gp[j]));
a->peq[i] = pow (10.0, 0.05 * (frac * a->ep[j + 1] + (1.0 - frac) * a->ep[j]));
a->cfc_gain[i] = a->precomplin * a->comp[i];
while (f >= fp[j + 1] && j < nfreqs) j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
comp[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j]));
peq[i] = pow (10.0, 0.05 * (frac * ep[j + 1] + (1.0 - frac) * ep[j]));
cfc_gain[i] = precomplin * comp[i];
}
// print_impulse ("comp.txt", a->msize, a->comp, 0, 0);
delete[] sary;
}
void CFCOMP::calc_cfcomp(CFCOMP *a)
void CFCOMP::calc_cfcomp()
{
int i;
a->incr = a->fsize / a->ovrlp;
if (a->fsize > a->bsize)
a->iasize = a->fsize;
incr = fsize / ovrlp;
if (fsize > bsize)
iasize = fsize;
else
a->iasize = a->bsize + a->fsize - a->incr;
a->iainidx = 0;
a->iaoutidx = 0;
if (a->fsize > a->bsize)
iasize = bsize + fsize - incr;
iainidx = 0;
iaoutidx = 0;
if (fsize > bsize)
{
if (a->bsize > a->incr) a->oasize = a->bsize;
else a->oasize = a->incr;
a->oainidx = (a->fsize - a->bsize - a->incr) % a->oasize;
if (bsize > incr) oasize = bsize;
else oasize = incr;
oainidx = (fsize - bsize - incr) % oasize;
}
else
{
a->oasize = a->bsize;
a->oainidx = a->fsize - a->incr;
oasize = bsize;
oainidx = fsize - incr;
}
a->init_oainidx = a->oainidx;
a->oaoutidx = 0;
a->msize = a->fsize / 2 + 1;
a->window = new float[a->fsize]; // (float *)malloc0 (a->fsize * sizeof(float));
a->inaccum = new float[a->iasize]; // (float *)malloc0 (a->iasize * sizeof(float));
a->forfftin = new float[a->fsize]; // (float *)malloc0 (a->fsize * sizeof(float));
a->forfftout = new float[a->msize * 2]; // (float *)malloc0 (a->msize * sizeof(complex));
a->cmask = new float[a->msize]; // (float *)malloc0 (a->msize * sizeof(float));
a->mask = new float[a->msize]; // (float *)malloc0 (a->msize * sizeof(float));
a->cfc_gain = new float[a->msize]; // (float *)malloc0 (a->msize * sizeof(float));
a->revfftin = new float[a->msize * 2]; // (float *)malloc0 (a->msize * sizeof(complex));
a->revfftout = new float[a->fsize]; // (float *)malloc0 (a->fsize * sizeof(float));
a->save = new float*[a->ovrlp]; // (float **)malloc0(a->ovrlp * sizeof(float *));
for (i = 0; i < a->ovrlp; i++)
a->save[i] = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float));
a->outaccum = new float[a->oasize]; // (float *)malloc0(a->oasize * sizeof(float));
a->nsamps = 0;
a->saveidx = 0;
a->Rfor = fftwf_plan_dft_r2c_1d(a->fsize, a->forfftin, (fftwf_complex *)a->forfftout, FFTW_ESTIMATE);
a->Rrev = fftwf_plan_dft_c2r_1d(a->fsize, (fftwf_complex *)a->revfftin, a->revfftout, FFTW_ESTIMATE);
calc_cfcwindow(a);
init_oainidx = oainidx;
oaoutidx = 0;
msize = fsize / 2 + 1;
window.resize(fsize);
inaccum.resize(iasize);
forfftin.resize(fsize);
forfftout.resize(msize * 2);
cmask.resize(msize);
mask.resize(msize);
cfc_gain.resize(msize);
revfftin.resize(msize * 2);
revfftout.resize(fsize);
save.resize(ovrlp);
for (int i = 0; i < ovrlp; i++)
save[i].resize(fsize);
outaccum.resize(oasize);
nsamps = 0;
saveidx = 0;
Rfor = fftwf_plan_dft_r2c_1d(fsize, forfftin.data(), (fftwf_complex *)forfftout.data(), FFTW_ESTIMATE);
Rrev = fftwf_plan_dft_c2r_1d(fsize, (fftwf_complex *)revfftin.data(), revfftout.data(), FFTW_ESTIMATE);
calc_cfcwindow();
a->pregain = (2.0 * a->winfudge) / (float)a->fsize;
a->postgain = 0.5 / ((float)a->ovrlp * a->winfudge);
pregain = (2.0 * winfudge) / (double)fsize;
postgain = 0.5 / ((double)ovrlp * winfudge);
a->fp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float));
a->gp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float));
a->ep = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float));
a->comp = new float[a->msize]; // (float *) malloc0 (a->msize * sizeof (float));
a->peq = new float[a->msize]; // (float *) malloc0 (a->msize * sizeof (float));
calc_comp (a);
fp.resize(nfreqs + 2);
gp.resize(nfreqs + 2);
ep.resize(nfreqs + 2);
comp.resize(msize);
peq.resize(msize);
calc_comp();
a->gain = 0.0;
a->mmult = exp (-1.0 / (a->rate * a->ovrlp * a->mtau));
a->dmult = exp (-(float)a->fsize / (a->rate * a->ovrlp * a->dtau));
gain = 0.0;
mmult = exp (-1.0 / (rate * ovrlp * mtau));
dmult = exp (-(float)fsize / (rate * ovrlp * dtau));
a->delta = new float[a->msize]; // (float*)malloc0 (a->msize * sizeof(float));
a->delta_copy = new float[a->msize]; // (float*)malloc0 (a->msize * sizeof(float));
a->cfc_gain_copy = new float[a->msize]; // (float*)malloc0 (a->msize * sizeof(float));
delta.resize(msize);
delta_copy.resize(msize);
cfc_gain_copy.resize(msize);
}
void CFCOMP::decalc_cfcomp(CFCOMP *a)
void CFCOMP::decalc_cfcomp()
{
fftwf_destroy_plan(Rrev);
fftwf_destroy_plan(Rfor);
}
CFCOMP::CFCOMP(
int _run,
int _position,
int _peq_run,
int _size,
float* _in,
float* _out,
int _fsize,
int _ovrlp,
int _rate,
int _wintype,
int _comp_method,
int _nfreqs,
double _precomp,
double _prepeq,
const double* _F,
const double* _G,
const double* _E,
double _mtau,
double _dtau
) :
run (_run),
position(_position),
bsize(_size),
in(_in),
out(_out),
fsize(_fsize),
ovrlp(_ovrlp),
rate(_rate),
wintype(_wintype),
comp_method(_comp_method),
nfreqs(_nfreqs),
precomp(_precomp),
peq_run(_peq_run),
prepeq(_prepeq),
mtau(_mtau), // compression metering time constant
dtau(_dtau) // compression display time constant
{
F.resize(nfreqs);
G.resize(nfreqs);
E.resize(nfreqs);
std::copy(_F, _F + nfreqs, F.begin());
std::copy(_G, _G + nfreqs, G.begin());
std::copy(_E, _E + nfreqs, E.begin());
calc_cfcomp();
}
CFCOMP::~CFCOMP()
{
decalc_cfcomp();
}
void CFCOMP::flush()
{
std::fill(inaccum.begin(), inaccum.end(), 0);
for (int i = 0; i < ovrlp; i++)
std::fill(save[i].begin(), save[i].end(), 0);
std::fill(outaccum.begin(), outaccum.end(), 0);
nsamps = 0;
iainidx = 0;
iaoutidx = 0;
oainidx = init_oainidx;
oaoutidx = 0;
saveidx = 0;
gain = 0.0;
std::fill(delta.begin(), delta.end(), 0);
}
void CFCOMP::calc_mask()
{
int i;
delete[] (a->cfc_gain_copy);
delete[] (a->delta_copy);
delete[] (a->delta);
delete[] (a->peq);
delete[] (a->comp);
delete[] (a->ep);
delete[] (a->gp);
delete[] (a->fp);
fftwf_destroy_plan(a->Rrev);
fftwf_destroy_plan(a->Rfor);
delete[](a->outaccum);
for (i = 0; i < a->ovrlp; i++)
delete[](a->save[i]);
delete[](a->save);
delete[](a->revfftout);
delete[](a->revfftin);
delete[](a->cfc_gain);
delete[](a->mask);
delete[](a->cmask);
delete[](a->forfftout);
delete[](a->forfftin);
delete[](a->inaccum);
delete[](a->window);
}
CFCOMP* CFCOMP::create_cfcomp (int run, int position, int peq_run, int size, float* in, float* out, int fsize, int ovrlp,
int rate, int wintype, int comp_method, int nfreqs, float precomp, float prepeq, float* F, float* G, float* E, float mtau, float dtau)
{
CFCOMP *a = new CFCOMP;
a->run = run;
a->position = position;
a->peq_run = peq_run;
a->bsize = size;
a->in = in;
a->out = out;
a->fsize = fsize;
a->ovrlp = ovrlp;
a->rate = rate;
a->wintype = wintype;
a->comp_method = comp_method;
a->nfreqs = nfreqs;
a->precomp = precomp;
a->prepeq = prepeq;
a->mtau = mtau; // compression metering time constant
a->dtau = dtau; // compression display time constant
a->F = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float));
a->G = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float));
a->E = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float));
memcpy (a->F, F, a->nfreqs * sizeof (float));
memcpy (a->G, G, a->nfreqs * sizeof (float));
memcpy (a->E, E, a->nfreqs * sizeof (float));
calc_cfcomp (a);
return a;
}
void CFCOMP::flush_cfcomp (CFCOMP *a)
{
int i;
memset (a->inaccum, 0, a->iasize * sizeof (float));
for (i = 0; i < a->ovrlp; i++)
memset (a->save[i], 0, a->fsize * sizeof (float));
memset (a->outaccum, 0, a->oasize * sizeof (float));
a->nsamps = 0;
a->iainidx = 0;
a->iaoutidx = 0;
a->oainidx = a->init_oainidx;
a->oaoutidx = 0;
a->saveidx = 0;
a->gain = 0.0;
memset(a->delta, 0, a->msize * sizeof(float));
}
void CFCOMP::destroy_cfcomp (CFCOMP *a)
{
decalc_cfcomp (a);
delete[] (a->E);
delete[] (a->G);
delete[] (a->F);
delete (a);
}
void CFCOMP::calc_mask (CFCOMP *a)
{
int i;
float comp, mask, delta;
switch (a->comp_method)
double _comp;
double _mask;
double _delta;
if (comp_method == 0)
{
case 0:
double mag;
double test;
for (i = 0; i < msize; i++)
{
float mag, test;
for (i = 0; i < a->msize; i++)
{
mag = sqrt (a->forfftout[2 * i + 0] * a->forfftout[2 * i + 0]
+ a->forfftout[2 * i + 1] * a->forfftout[2 * i + 1]);
comp = a->cfc_gain[i];
test = comp * mag;
if (test > 1.0)
mask = 1.0 / mag;
else
mask = comp;
a->cmask[i] = mask;
if (test > a->gain) a->gain = test;
else a->gain = a->mmult * a->gain;
mag = sqrt (forfftout[2 * i + 0] * forfftout[2 * i + 0]
+ forfftout[2 * i + 1] * forfftout[2 * i + 1]);
_comp = cfc_gain[i];
test = _comp * mag;
if (test > 1.0)
_mask = 1.0 / mag;
else
_mask = _comp;
cmask[i] = _mask;
if (test > gain) gain = test;
else gain = mmult * gain;
delta = a->cfc_gain[i] - a->cmask[i];
if (delta > a->delta[i]) a->delta[i] = delta;
else a->delta[i] *= a->dmult;
_delta = cfc_gain[i] - cmask[i];
if (_delta > delta[i]) delta[i] = _delta;
else delta[i] *= dmult;
}
}
if (peq_run)
{
for (i = 0; i < msize; i++)
{
mask[i] = cmask[i] * prepeqlin * peq[i];
}
}
else
std::copy(cmask.begin(), cmask.end(), mask.begin());
mask_ready = 1;
}
void CFCOMP::execute(int pos)
{
if (run && pos == position)
{
int i;
int j;
int k;
int sbuff;
int sbegin;
for (i = 0; i < 2 * bsize; i += 2)
{
inaccum[iainidx] = in[i];
iainidx = (iainidx + 1) % iasize;
}
nsamps += bsize;
while (nsamps >= fsize)
{
for (i = 0, j = iaoutidx; i < fsize; i++, j = (j + 1) % iasize)
forfftin[i] = (float) (pregain * window[i] * inaccum[j]);
iaoutidx = (iaoutidx + incr) % iasize;
nsamps -= incr;
fftwf_execute (Rfor);
calc_mask();
for (i = 0; i < msize; i++)
{
revfftin[2 * i + 0] = (float) (mask[i] * forfftout[2 * i + 0]);
revfftin[2 * i + 1] = (float) (mask[i] * forfftout[2 * i + 1]);
}
break;
}
}
if (a->peq_run)
{
for (i = 0; i < a->msize; i++)
{
a->mask[i] = a->cmask[i] * a->prepeqlin * a->peq[i];
}
}
else
memcpy (a->mask, a->cmask, a->msize * sizeof (float));
// print_impulse ("mask.txt", a->msize, a->mask, 0, 0);
a->mask_ready = 1;
}
void CFCOMP::xcfcomp (CFCOMP *a, int pos)
{
if (a->run && pos == a->position)
{
int i, j, k, sbuff, sbegin;
for (i = 0; i < 2 * a->bsize; i += 2)
{
a->inaccum[a->iainidx] = a->in[i];
a->iainidx = (a->iainidx + 1) % a->iasize;
}
a->nsamps += a->bsize;
while (a->nsamps >= a->fsize)
{
for (i = 0, j = a->iaoutidx; i < a->fsize; i++, j = (j + 1) % a->iasize)
a->forfftin[i] = a->pregain * a->window[i] * a->inaccum[j];
a->iaoutidx = (a->iaoutidx + a->incr) % a->iasize;
a->nsamps -= a->incr;
fftwf_execute (a->Rfor);
calc_mask(a);
for (i = 0; i < a->msize; i++)
fftwf_execute (Rrev);
for (i = 0; i < fsize; i++)
save[saveidx][i] = postgain * window[i] * revfftout[i];
for (i = ovrlp; i > 0; i--)
{
a->revfftin[2 * i + 0] = a->mask[i] * a->forfftout[2 * i + 0];
a->revfftin[2 * i + 1] = a->mask[i] * a->forfftout[2 * i + 1];
}
fftwf_execute (a->Rrev);
for (i = 0; i < a->fsize; i++)
a->save[a->saveidx][i] = a->postgain * a->window[i] * a->revfftout[i];
for (i = a->ovrlp; i > 0; i--)
{
sbuff = (a->saveidx + i) % a->ovrlp;
sbegin = a->incr * (a->ovrlp - i);
for (j = sbegin, k = a->oainidx; j < a->incr + sbegin; j++, k = (k + 1) % a->oasize)
sbuff = (saveidx + i) % ovrlp;
sbegin = incr * (ovrlp - i);
for (j = sbegin, k = oainidx; j < incr + sbegin; j++, k = (k + 1) % oasize)
{
if ( i == a->ovrlp)
a->outaccum[k] = a->save[sbuff][j];
if ( i == ovrlp)
outaccum[k] = save[sbuff][j];
else
a->outaccum[k] += a->save[sbuff][j];
outaccum[k] += save[sbuff][j];
}
}
a->saveidx = (a->saveidx + 1) % a->ovrlp;
a->oainidx = (a->oainidx + a->incr) % a->oasize;
saveidx = (saveidx + 1) % ovrlp;
oainidx = (oainidx + incr) % oasize;
}
for (i = 0; i < a->bsize; i++)
for (i = 0; i < bsize; i++)
{
a->out[2 * i + 0] = a->outaccum[a->oaoutidx];
a->out[2 * i + 1] = 0.0;
a->oaoutidx = (a->oaoutidx + 1) % a->oasize;
out[2 * i + 0] = (float) (outaccum[oaoutidx]);
out[2 * i + 1] = 0.0;
oaoutidx = (oaoutidx + 1) % oasize;
}
}
else if (a->out != a->in)
std::copy(a->in, a->in + a->bsize * 2, a->out);
else if (out != in)
std::copy(in, in + bsize * 2, out);
}
void CFCOMP::setBuffers_cfcomp (CFCOMP *a, float* in, float* out)
void CFCOMP::setBuffers(float* _in, float* _out)
{
a->in = in;
a->out = out;
in = _in;
out = _out;
}
void CFCOMP::setSamplerate_cfcomp (CFCOMP *a, int rate)
void CFCOMP::setSamplerate(int _rate)
{
decalc_cfcomp (a);
a->rate = rate;
calc_cfcomp (a);
decalc_cfcomp();
rate = _rate;
calc_cfcomp();
}
void CFCOMP::setSize_cfcomp (CFCOMP *a, int size)
void CFCOMP::setSize(int size)
{
decalc_cfcomp (a);
a->bsize = size;
calc_cfcomp (a);
decalc_cfcomp();
bsize = size;
calc_cfcomp();
}
/********************************************************************************************************
@ -415,94 +418,75 @@ void CFCOMP::setSize_cfcomp (CFCOMP *a, int size)
* *
********************************************************************************************************/
void CFCOMP::SetCFCOMPRun (TXA& txa, int run)
void CFCOMP::setRun(int _run)
{
CFCOMP *a = txa.cfcomp.p;
if (a->run != run) {
a->run = run;
if (run != _run) {
run = _run;
}
}
void CFCOMP::SetCFCOMPPosition (TXA& txa, int pos)
void CFCOMP::setPosition(int pos)
{
CFCOMP *a = txa.cfcomp.p;
if (a->position != pos) {
a->position = pos;
if (position != pos) {
position = pos;
}
}
void CFCOMP::SetCFCOMPprofile (TXA& txa, int nfreqs, float* F, float* G, float *E)
void CFCOMP::setProfile(int _nfreqs, const double* _F, const double* _G, const double* _E)
{
CFCOMP *a = txa.cfcomp.p;
a->nfreqs = nfreqs < 1 ? 1 : nfreqs;
delete[] (a->E);
delete[] (a->F);
delete[] (a->G);
a->F = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float));
a->G = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float));
a->E = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float));
memcpy (a->F, F, a->nfreqs * sizeof (float));
memcpy (a->G, G, a->nfreqs * sizeof (float));
memcpy (a->E, E, a->nfreqs * sizeof (float));
delete[] (a->ep);
delete[] (a->gp);
delete[] (a->fp);
a->fp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float));
a->gp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float));
a->ep = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float));
calc_comp(a);
nfreqs = _nfreqs < 1 ? 1 : _nfreqs;
F.resize(nfreqs);
G.resize(nfreqs);
E.resize(nfreqs);
std::copy(_F, _F + nfreqs, F.begin());
std::copy(_G, _G + nfreqs, G.begin());
std::copy(_E, _E + nfreqs, E.begin());
fp.resize(nfreqs + 2);
gp.resize(nfreqs + 2);
ep.resize(nfreqs + 2);
calc_comp();
}
void CFCOMP::SetCFCOMPPrecomp (TXA& txa, float precomp)
void CFCOMP::setPrecomp(double _precomp)
{
CFCOMP *a = txa.cfcomp.p;
if (a->precomp != precomp)
if (precomp != _precomp)
{
a->precomp = precomp;
a->precomplin = pow (10.0, 0.05 * a->precomp);
precomp = _precomp;
precomplin = pow (10.0, 0.05 * precomp);
for (int i = 0; i < a->msize; i++)
for (int i = 0; i < msize; i++)
{
a->cfc_gain[i] = a->precomplin * a->comp[i];
cfc_gain[i] = precomplin * comp[i];
}
}
}
void CFCOMP::SetCFCOMPPeqRun (TXA& txa, int run)
void CFCOMP::setPeqRun(int _run)
{
CFCOMP *a = txa.cfcomp.p;
if (a->peq_run != run) {
a->peq_run = run;
if (peq_run != _run) {
peq_run = _run;
}
}
void CFCOMP::SetCFCOMPPrePeq (TXA& txa, float prepeq)
void CFCOMP::setPrePeq(double _prepeq)
{
CFCOMP *a = txa.cfcomp.p;
a->prepeq = prepeq;
a->prepeqlin = pow (10.0, 0.05 * a->prepeq);
prepeq = _prepeq;
prepeqlin = pow (10.0, 0.05 * prepeq);
}
void CFCOMP::GetCFCOMPDisplayCompression (TXA& txa, float* comp_values, int* ready)
void CFCOMP::getDisplayCompression(double* comp_values, int* ready)
{
int i;
CFCOMP *a = txa.cfcomp.p;
if ((*ready = a->mask_ready))
if ((*ready = mask_ready))
{
memcpy(a->delta_copy, a->delta, a->msize * sizeof(float));
memcpy(a->cfc_gain_copy, a->cfc_gain, a->msize * sizeof(float));
a->mask_ready = 0;
std::copy(delta.begin(), delta.end(), delta_copy.begin());
std::copy(cfc_gain.begin(), cfc_gain.end(), cfc_gain_copy.begin());
mask_ready = 0;
}
if (*ready)
{
for (i = 0; i < a->msize; i++)
comp_values[i] = 20.0 * MemLog::mlog10 (a->cfc_gain_copy[i] / (a->cfc_gain_copy[i] - a->delta_copy[i]));
for (int i = 0; i < msize; i++)
comp_values[i] = 20.0 * MemLog::mlog10 (cfc_gain_copy[i] / (cfc_gain_copy[i] - delta_copy[i]));
}
}

View File

@ -28,6 +28,8 @@ warren@wpratt.com
#ifndef wdsp_cfcomp_h
#define wdsp_cfcomp_h
#include <vector>
#include "fftw3.h"
#include "export.h"
@ -46,25 +48,25 @@ public:
int fsize;
int ovrlp;
int incr;
float* window;
std::vector<double> window;
int iasize;
float* inaccum;
float* forfftin;
float* forfftout;
std::vector<double> inaccum;
std::vector<float> forfftin;
std::vector<float> forfftout;
int msize;
float* cmask;
float* mask;
std::vector<double> cmask;
std::vector<double> mask;
int mask_ready;
float* cfc_gain;
float* revfftin;
float* revfftout;
float** save;
std::vector<double> cfc_gain;
std::vector<float> revfftin;
std::vector<float> revfftout;
std::vector<std::vector<double>> save;
int oasize;
float* outaccum;
float rate;
std::vector<double> outaccum;
double rate;
int wintype;
float pregain;
float postgain;
double pregain;
double postgain;
int nsamps;
int iainidx;
int iaoutidx;
@ -77,32 +79,32 @@ public:
int comp_method;
int nfreqs;
float* F;
float* G;
float* E;
float* fp;
float* gp;
float* ep;
float* comp;
float precomp;
float precomplin;
float* peq;
std::vector<double> F;
std::vector<double> G;
std::vector<double> E;
std::vector<double> fp;
std::vector<double> gp;
std::vector<double> ep;
std::vector<double> comp;
double precomp;
double precomplin;
std::vector<double> peq;
int peq_run;
float prepeq;
float prepeqlin;
float winfudge;
double prepeq;
double prepeqlin;
double winfudge;
float gain;
float mtau;
float mmult;
double gain;
double mtau;
double mmult;
// display stuff
float dtau;
float dmult;
float* delta;
float* delta_copy;
float* cfc_gain_copy;
double dtau;
double dmult;
std::vector<double> delta;
std::vector<double> delta_copy;
std::vector<double> cfc_gain_copy;
static CFCOMP* create_cfcomp (
CFCOMP(
int run,
int position,
int peq_run,
@ -115,36 +117,39 @@ public:
int wintype,
int comp_method,
int nfreqs,
float precomp,
float prepeq,
float* F,
float* G,
float* E,
float mtau,
float dtau
double precomp,
double prepeq,
const double* F,
const double* G,
const double* E,
double mtau,
double dtau
);
static void destroy_cfcomp (CFCOMP *a);
static void flush_cfcomp (CFCOMP *a);
static void xcfcomp (CFCOMP *a, int pos);
static void setBuffers_cfcomp (CFCOMP *a, float* in, float* out);
static void setSamplerate_cfcomp (CFCOMP *a, int rate);
static void setSize_cfcomp (CFCOMP *a, int size);
CFCOMP(const CFCOMP&) = delete;
CFCOMP& operator=(CFCOMP& other) = delete;
~CFCOMP();
void flush();
void execute(int pos);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// TXA Properties
static void SetCFCOMPRun (TXA& txa, int run);
static void SetCFCOMPPosition (TXA& txa, int pos);
static void SetCFCOMPprofile (TXA& txa, int nfreqs, float* F, float* G, float *E);
static void SetCFCOMPPrecomp (TXA& txa, float precomp);
static void SetCFCOMPPeqRun (TXA& txa, int run);
static void SetCFCOMPPrePeq (TXA& txa, float prepeq);
static void GetCFCOMPDisplayCompression (TXA& txa, float* comp_values, int* ready);
void setRun(int run);
void setPosition(int pos);
void setProfile(int nfreqs, const double* F, const double* G, const double *E);
void setPrecomp(double precomp);
void setPeqRun(int run);
void setPrePeq(double prepeq);
void getDisplayCompression(double* comp_values, int* ready);
private:
static void calc_cfcwindow (CFCOMP *a);
void calc_cfcwindow();
static int fCOMPcompare (const void *a, const void *b);
static void calc_comp (CFCOMP *a);
static void calc_cfcomp(CFCOMP *a);
static void decalc_cfcomp(CFCOMP *a);
static void calc_mask (CFCOMP *a);
void calc_comp();
void calc_cfcomp();
void decalc_cfcomp();
void calc_mask();
};
} // namespace WDSP

View File

@ -32,36 +32,35 @@ warren@wpratt.com
namespace WDSP {
void CFIR::calc_cfir (CFIR *a)
void CFIR::calc()
{
float* impulse;
a->scale = 1.0 / (float)(2 * a->size);
impulse = cfir_impulse (a->nc, a->DD, a->R, a->Pairs, a->runrate, a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype);
a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
delete[] (impulse);
std::vector<float> impulse;
scale = 1.0 / (float)(2 * size);
cfir_impulse (impulse, nc, DD, R, Pairs, runrate, cicrate, cutoff, xtype, xbw, 1, scale, wintype);
p = new FIRCORE(size, in, out, mp, impulse);
}
void CFIR::decalc_cfir (CFIR *a)
void CFIR::decalc()
{
FIRCORE::destroy_fircore (a->p);
delete p;
}
CFIR* CFIR::create_cfir (
int run,
int size,
int nc,
int mp,
float* in,
float* out,
int runrate,
int cicrate,
int DD,
int R,
int Pairs,
double cutoff,
int xtype,
double xbw,
int wintype
CFIR::CFIR(
int _run,
int _size,
int _nc,
int _mp,
float* _in,
float* _out,
int _runrate,
int _cicrate,
int _DD,
int _R,
int _Pairs,
double _cutoff,
int _xtype,
double _xbw,
int _wintype
)
// run: 0 - no action; 1 - operate
// size: number of complex samples in an input buffer to the CFIR filter
@ -77,87 +76,85 @@ CFIR* CFIR::create_cfir (
// xtype: 0 - fourth power transition; 1 - raised cosine transition; 2 - brick wall
// xbw: width of raised cosine transition
{
CFIR *a = new CFIR;
a->run = run;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->runrate = runrate;
a->cicrate = cicrate;
a->DD = DD;
a->R = R;
a->Pairs = Pairs;
a->cutoff = cutoff;
a->xtype = xtype;
a->xbw = xbw;
a->wintype = wintype;
calc_cfir (a);
return a;
run = _run;
size = _size;
nc = _nc;
mp = _mp;
in = _in;
out = _out;
runrate = _runrate;
cicrate = _cicrate;
DD = _DD;
R = _R;
Pairs = _Pairs;
cutoff = _cutoff;
xtype = _xtype;
xbw = _xbw;
wintype = _wintype;
calc();
}
void CFIR::destroy_cfir (CFIR *a)
CFIR::~CFIR()
{
decalc_cfir (a);
delete (a);
decalc();
}
void CFIR::flush_cfir (CFIR *a)
void CFIR::flush()
{
FIRCORE::flush_fircore (a->p);
p->flush();
}
void CFIR::xcfir (CFIR *a)
void CFIR::execute()
{
if (a->run)
FIRCORE::xfircore (a->p);
else if (a->in != a->out)
std::copy( a->in, a->in + a->size * 2, a->out);
if (run)
p->execute();
else if (in != out)
std::copy( in, in + size * 2, out);
}
void CFIR::setBuffers_cfir (CFIR *a, float* in, float* out)
void CFIR::setBuffers(float* _in, float* _out)
{
decalc_cfir (a);
a->in = in;
a->out = out;
calc_cfir (a);
decalc();
in = _in;
out = _out;
calc();
}
void CFIR::setSamplerate_cfir (CFIR *a, int rate)
void CFIR::setSamplerate(int rate)
{
decalc_cfir (a);
a->runrate = rate;
calc_cfir (a);
decalc();
runrate = rate;
calc();
}
void CFIR::setSize_cfir (CFIR *a, int size)
void CFIR::setSize(int _size)
{
decalc_cfir (a);
a->size = size;
calc_cfir (a);
decalc();
size = _size;
calc();
}
void CFIR::setOutRate_cfir (CFIR *a, int rate)
void CFIR::setOutRate(int rate)
{
decalc_cfir (a);
a->cicrate = rate;
calc_cfir (a);
decalc();
cicrate = rate;
calc();
}
float* CFIR::cfir_impulse (
int N,
int DD,
int R,
int Pairs,
double runrate,
double cicrate,
double cutoff,
int xtype,
double xbw,
int rtype,
double scale,
int wintype
void CFIR::cfir_impulse (
std::vector<float>& impulse,
int _N,
int _DD,
int _R,
int _Pairs,
double _runrate,
double _cicrate,
double _cutoff,
int _xtype,
double _xbw,
int _rtype,
double _scale,
int _wintype
)
{
// N: number of impulse response samples
@ -171,92 +168,93 @@ float* CFIR::cfir_impulse (
// xbw: transition bandwidth for raised cosine
// rtype: 0 for real output, 1 for complex output
// scale: scale factor to be applied to the output
int i, j;
double tmp, local_scale, ri, mag, fn;
float* impulse;
float* A = new float[N]; // (float *) malloc0 (N * sizeof (float));
double ft = cutoff / cicrate; // normalized cutoff frequency
int u_samps = (N + 1) / 2; // number of unique samples, OK for odd or even N
int c_samps = (int)(cutoff / runrate * N) + (N + 1) / 2 - N / 2; // number of unique samples within bandpass, OK for odd or even N
int x_samps = (int)(xbw / runrate * N); // number of unique samples in transition region, OK for odd or even N
double offset = 0.5 - 0.5 * (float)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N
double* xistion = new double[x_samps + 1]; // (float *) malloc0 ((x_samps + 1) * sizeof (float));
double delta = PI / (float)x_samps;
double L = cicrate / runrate;
double phs = 0.0;
int i;
int j;
double tmp;
double local_scale;
double ri;
double mag = 0;
double fn;
std::vector<float> A(_N);
double ft = _cutoff / _cicrate; // normalized cutoff frequency
int u_samps = (_N + 1) / 2; // number of unique samples, OK for odd or even N
int c_samps = (int)(_cutoff / _runrate * _N) + (_N + 1) / 2 - _N / 2; // number of unique samples within bandpass, OK for odd or even N
auto x_samps = (int)(_xbw / _runrate * _N); // number of unique samples in transition region, OK for odd or even N
double offset = 0.5 - 0.5 * (double)((_N + 1) / 2 - _N / 2); // sample offset from center, OK for odd or even N
std::vector<double> xistion(x_samps + 1);
double delta = PI / (double)x_samps;
double L = _cicrate / _runrate;
double _phs = 0.0;
for (i = 0; i <= x_samps; i++)
{
xistion[i] = 0.5 * (cos (phs) + 1.0);
phs += delta;
xistion[i] = 0.5 * (cos (_phs) + 1.0);
_phs += delta;
}
if ((tmp = DD * R * sin (PI * ft / R) / sin (PI * DD * ft)) < 0.0) //normalize by peak gain
if ((tmp = _DD * _R * sin (PI * ft / _R) / sin (PI * _DD * ft)) < 0.0) //normalize by peak gain
tmp = -tmp;
local_scale = scale / pow (tmp, Pairs);
if (xtype == 0)
local_scale = _scale / pow (tmp, _Pairs);
if (_xtype == 0)
{
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
{
fn = ri / (L * (float)N);
fn = ri / (L * (double) _N);
if (fn <= ft)
{
if (fn == 0.0)
tmp = 1.0;
else if ((tmp = DD * R * sin (PI * fn / R) / sin (PI * DD * fn)) < 0.0)
else if ((tmp = _DD * _R * sin (PI * fn / _R) / sin (PI * _DD * fn)) < 0.0)
tmp = -tmp;
mag = pow (tmp, Pairs) * local_scale;
mag = pow (tmp, _Pairs) * local_scale;
}
else
mag *= (ft * ft * ft * ft) / (fn * fn * fn * fn);
A[i] = mag;
A[i] = (float) mag;
}
}
else if (xtype == 1)
else if (_xtype == 1)
{
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
{
fn = ri / (L *(float)N);
fn = ri / (L *(double) _N);
if (i < c_samps)
{
if (fn == 0.0) tmp = 1.0;
else if ((tmp = DD * R * sin (PI * fn / R) / sin (PI * DD * fn)) < 0.0)
else if ((tmp = _DD * _R * sin (PI * fn / _R) / sin (PI * _DD * fn)) < 0.0)
tmp = -tmp;
mag = pow (tmp, Pairs) * local_scale;
A[i] = mag;
mag = pow (tmp, _Pairs) * local_scale;
A[i] = (float) mag;
}
else if ( i >= c_samps && i <= c_samps + x_samps)
A[i] = mag * xistion[i - c_samps];
A[i] = (float) (mag * xistion[i - c_samps]);
else
A[i] = 0.0;
}
}
else if (xtype == 2)
else if (_xtype == 2)
{
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
{
fn = ri / (L * (float)N);
fn = ri / (L * (double) _N);
if (fn <= ft)
{
if (fn == 0.0) tmp = 1.0;
else if ((tmp = DD * R * sin(PI * fn / R) / sin(PI * DD * fn)) < 0.0)
else if ((tmp = _DD * _R * sin(PI * fn / _R) / sin(PI * _DD * fn)) < 0.0)
tmp = -tmp;
mag = pow (tmp, Pairs) * local_scale;
mag = pow (tmp, _Pairs) * local_scale;
}
else
mag = 0.0;
A[i] = mag;
A[i] = (float) mag;
}
}
if (N & 1)
for (i = u_samps, j = 2; i < N; i++, j++)
if (_N & 1)
for (i = u_samps, j = 2; i < _N; i++, j++)
A[i] = A[u_samps - j];
else
for (i = u_samps, j = 1; i < N; i++, j++)
for (i = u_samps, j = 1; i < _N; i++, j++)
A[i] = A[u_samps - j];
impulse = FIR::fir_fsamp (N, A, rtype, 1.0, wintype);
// print_impulse ("cfirImpulse.txt", N, impulse, 1, 0);
delete[] A;
delete[] xistion;
return impulse;
impulse.resize(2 * _N);
FIR::fir_fsamp (impulse, _N, A.data(), _rtype, 1.0, _wintype);
}
/********************************************************************************************************
@ -265,22 +263,20 @@ float* CFIR::cfir_impulse (
* *
********************************************************************************************************/
void CFIR::SetCFIRRun (TXA& txa, int run)
void CFIR::setRun(int _run)
{
txa.cfir.p->run = run;
run = _run;
}
void CFIR::SetCFIRNC(TXA& txa, int nc)
void CFIR::setNC(int _nc)
{
// NOTE: 'nc' must be >= 'size'
CFIR *a;
a = txa.cfir.p;
if (a->nc != nc)
if (nc != _nc)
{
a->nc = nc;
decalc_cfir(a);
calc_cfir(a);
nc = _nc;
decalc();
calc();
}
}

View File

@ -28,6 +28,8 @@ warren@wpratt.com
#ifndef wdsp_cfir_h
#define wdsp_cfir_h
#include <vector>
#include "export.h"
namespace WDSP {
@ -56,7 +58,7 @@ public:
int wintype;
FIRCORE *p;
static CFIR* create_cfir (
CFIR(
int run,
int size,
int nc,
@ -73,14 +75,18 @@ public:
double xbw,
int wintype
);
static void destroy_cfir (CFIR *a);
static void flush_cfir (CFIR *a);
static void xcfir (CFIR *a);
static void setBuffers_cfir (CFIR *a, float* in, float* out);
static void setSamplerate_cfir (CFIR *a, int rate);
static void setSize_cfir (CFIR *a, int size);
static void setOutRate_cfir (CFIR *a, int rate);
static float* cfir_impulse (
CFIR(const CFIR&) = delete;
CFIR& operator=(CFIR& other) = delete;
~CFIR();
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
void setOutRate(int rate);
static void cfir_impulse (
std::vector<float>& impulse,
int N,
int DD,
int R,
@ -95,12 +101,12 @@ public:
int wintype
);
// TXA Properties
static void SetCFIRRun(TXA& txa, int run);
static void SetCFIRNC(TXA& txa, int nc);
void setRun(int run);
void setNC(int nc);
private:
static void calc_cfir (CFIR *a);
static void decalc_cfir (CFIR *a);
void calc();
void decalc();
};
} // namespace WDSP

View File

@ -34,62 +34,56 @@ in the January 2010 issue of RadCom magazine.
namespace WDSP {
COMPRESSOR* COMPRESSOR::create_compressor (
int run,
int buffsize,
float* inbuff,
float* outbuff,
float gain )
COMPRESSOR::COMPRESSOR(
int _run,
int _buffsize,
float* _inbuff,
float* _outbuff,
double _gain
) :
run(_run),
buffsize(_buffsize),
inbuff(_inbuff),
outbuff(_outbuff),
gain(_gain)
{}
void COMPRESSOR::flush()
{
COMPRESSOR *a = new COMPRESSOR;
a->run = run;
a->inbuff = inbuff;
a->outbuff = outbuff;
a->buffsize = buffsize;
a->gain = gain;
return a;
// Nothing to do
}
void COMPRESSOR::destroy_compressor (COMPRESSOR *a)
void COMPRESSOR::execute()
{
delete (a);
}
void COMPRESSOR::flush_compressor (COMPRESSOR *)
{
}
void COMPRESSOR::xcompressor (COMPRESSOR *a)
{
int i;
float mag;
if (a->run)
for (i = 0; i < a->buffsize; i++)
double mag;
if (run)
for (int i = 0; i < buffsize; i++)
{
mag = sqrt(a->inbuff[2 * i + 0] * a->inbuff[2 * i + 0] + a->inbuff[2 * i + 1] * a->inbuff[2 * i + 1]);
if (a->gain * mag > 1.0)
a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] / mag;
mag = sqrt(inbuff[2 * i + 0] * inbuff[2 * i + 0] + inbuff[2 * i + 1] * inbuff[2 * i + 1]);
if (gain * mag > 1.0)
outbuff[2 * i + 0] = (float) (inbuff[2 * i + 0] / mag);
else
a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] * a->gain;
a->outbuff[2 * i + 1] = 0.0;
outbuff[2 * i + 0] = (float) (inbuff[2 * i + 0] * gain);
outbuff[2 * i + 1] = 0.0;
}
else if (a->inbuff != a->outbuff)
std::copy(a->inbuff, a->inbuff + a->buffsize * 2, a->outbuff);
else if (inbuff != outbuff)
std::copy(inbuff, inbuff + buffsize * 2, outbuff);
}
void COMPRESSOR::setBuffers_compressor (COMPRESSOR *a, float* in, float* out)
void COMPRESSOR::setBuffers(float* _in, float* _out)
{
a->inbuff = in;
a->outbuff = out;
inbuff = _in;
outbuff = _out;
}
void COMPRESSOR::setSamplerate_compressor (COMPRESSOR *, int)
void COMPRESSOR::setSamplerate(int)
{
// Nothing to do
}
void COMPRESSOR::setSize_compressor (COMPRESSOR *a, int size)
void COMPRESSOR::setSize(int _size)
{
a->buffsize = size;
buffsize = _size;
}
/********************************************************************************************************
@ -98,18 +92,9 @@ void COMPRESSOR::setSize_compressor (COMPRESSOR *a, int size)
* *
********************************************************************************************************/
void COMPRESSOR::SetCompressorRun (TXA& txa, int run)
void COMPRESSOR::setGain(float _gain)
{
if (txa.compressor.p->run != run)
{
txa.compressor.p->run = run;
TXA::SetupBPFilters (txa);
}
}
void COMPRESSOR::SetCompressorGain (TXA& txa, float gain)
{
txa.compressor.p->gain = pow (10.0, gain / 20.0);
gain = pow (10.0, _gain / 20.0);
}
} // namespace WDSP

View File

@ -41,24 +41,26 @@ public:
int buffsize;
float *inbuff;
float *outbuff;
float gain;
double gain;
static COMPRESSOR* create_compressor (
COMPRESSOR(
int run,
int buffsize,
float* inbuff,
float* outbuff,
float gain
double gain
);
static void destroy_compressor (COMPRESSOR *a);
static void flush_compressor (COMPRESSOR *a);
static void xcompressor (COMPRESSOR *a);
static void setBuffers_compressor (COMPRESSOR *a, float* in, float* out);
static void setSamplerate_compressor (COMPRESSOR *a, int rate);
static void setSize_compressor (COMPRESSOR *a, int size);
COMPRESSOR(const COMPRESSOR&) = delete;
COMPRESSOR& operator=(COMPRESSOR& other) = delete;
~COMPRESSOR() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// TXA Properties
static void SetCompressorRun (TXA& txa, int run);
static void SetCompressorGain (TXA& txa, float gain);
void setGain(float gain);
};
} // namespace WDSP

156
wdsp/dbqbp.cpp Normal file
View File

@ -0,0 +1,156 @@
/* dbqbp.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "dbqbp.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Double Bi-Quad Band-Pass *
* *
********************************************************************************************************/
void DBQBP::calc()
{
double f0;
double w0;
double bw;
double q;
double sn;
double cs;
double c;
double den;
bw = f_high - f_low;
f0 = (f_high + f_low) / 2.0;
q = f0 / bw;
w0 = TWOPI * f0 / rate;
sn = sin(w0);
cs = cos(w0);
c = sn / (2.0 * q);
den = 1.0 + c;
a0 = +c / den;
a1 = 0.0;
a2 = -c / den;
b1 = 2.0 * cs / den;
b2 = (c - 1.0) / den;
flush();
}
DBQBP::DBQBP(
int _run,
int _size,
float* _in,
float* _out,
double _rate,
double _f_low,
double _f_high,
double _gain,
int _nstages
) :
run(_run),
size(_size),
in(_in),
out(_out),
rate(_rate),
f_low(_f_low),
f_high(_f_high),
gain(_gain),
nstages(_nstages)
{
x0.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
x1.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
x2.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y0.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y1.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y2.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
calc();
}
void DBQBP::flush()
{
for (int i = 0; i < nstages; i++)
{
x1[i] = x2[i] = y1[i] = y2[i] = 0.0;
}
}
void DBQBP::execute()
{
if (run)
{
for (int i = 0; i < size; i++)
{
x0[0] = gain * in[i];
for (int n = 0; n < nstages; n++)
{
if (n > 0)
x0[n] = y0[n - 1];
y0[n] = a0 * x0[n]
+ a1 * x1[n]
+ a2 * x2[n]
+ b1 * y1[n]
+ b2 * y2[n];
y2[n] = y1[n];
y1[n] = y0[n];
x2[n] = x1[n];
x1[n] = x0[n];
}
out[i] = (float) y0[nstages - 1];
}
}
else if (out != in)
{
std::copy(in, in + size, out);
}
}
void DBQBP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
}
void DBQBP::setSamplerate(int _rate)
{
rate = _rate;
calc();
}
void DBQBP::setSize(int _size)
{
size = _size;
flush();
}
} // namespace WDSP

95
wdsp/dbqbp.hpp Normal file
View File

@ -0,0 +1,95 @@
/* dbqbp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Complex Bi-Quad Band-Pass *
* *
********************************************************************************************************/
#ifndef wdsp_dbqbp_h
#define wdsp_dbqbp_h
#include <vector>
#include "export.h"
namespace WDSP {
class WDSP_API DBQBP
{
public:
int run;
int size;
float* in;
float* out;
double rate;
double f_low;
double f_high;
double gain;
int nstages;
double a0;
double a1;
double a2;
double b1;
double b2;
std::vector<double> x0;
std::vector<double> x1;
std::vector<double> x2;
std::vector<double> y0;
std::vector<double> y1;
std::vector<double> y2;
// Double Bi-Quad Band-Pass
DBQBP(
int run,
int size,
float* in,
float* out,
double rate,
double f_low,
double f_high,
double gain,
int nstages
);
DBQBP(const DBQBP&) = delete;
DBQBP& operator=(DBQBP& other) = delete;
~DBQBP() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
void calc();
};
} // namespace WDSP
#endif

146
wdsp/dbqlp.cpp Normal file
View File

@ -0,0 +1,146 @@
/* dbqlp.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "dbqlp.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Double Bi-Quad Low-Pass *
* *
********************************************************************************************************/
void DBQLP::calc()
{
float w0, cs, c, den;
w0 = TWOPI * fc / (float)rate;
cs = cos(w0);
c = sin(w0) / (2.0 * Q);
den = 1.0 + c;
a0 = 0.5 * (1.0 - cs) / den;
a1 = (1.0 - cs) / den;
a2 = 0.5 * (1.0 - cs) / den;
b1 = 2.0 * cs / den;
b2 = (c - 1.0) / den;
flush();
}
DBQLP::DBQLP(
int _run,
int _size,
float* _in,
float* _out,
double _rate,
double _fc,
double _Q,
double _gain,
int _nstages
) :
run(_run),
size(_size),
in(_in),
out(_out),
rate(_rate),
fc(_fc),
Q(_Q),
gain(_gain),
nstages(_nstages)
{
x0.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
x1.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
x2.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y0.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y1.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y2.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
calc();
}
void DBQLP::flush()
{
for (int i = 0; i < nstages; i++)
{
x1[i] = x2[i] = y1[i] = y2[i] = 0.0;
}
}
void DBQLP::execute()
{
if (run)
{
int i, n;
for (i = 0; i < size; i++)
{
x0[0] = gain * in[i];
for (n = 0; n < nstages; n++)
{
if (n > 0)
x0[n] = y0[n - 1];
y0[n] = a0 * x0[n]
+ a1 * x1[n]
+ a2 * x2[n]
+ b1 * y1[n]
+ b2 * y2[n];
y2[n] = y1[n];
y1[n] = y0[n];
x2[n] = x1[n];
x1[n] = x0[n];
}
out[i] = y0[nstages - 1];
}
}
else if (out != in)
{
std::copy(in, in + size, out);
}
}
void DBQLP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
}
void DBQLP::setSamplerate(int _rate)
{
rate = _rate;
calc();
}
void DBQLP::setSize(int _size)
{
size = _size;
flush();
}
} // namespace WDSP

85
wdsp/dbqlp.hpp Normal file
View File

@ -0,0 +1,85 @@
/* dbqlp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Double Bi-Quad Low-Pass *
* *
********************************************************************************************************/
#ifndef wdsp_dbqlp_h
#define wdsp_dbqlp_h
#include <vector>
#include "export.h"
namespace WDSP {
class WDSP_API DBQLP
{
public:
int run;
int size;
float* in;
float* out;
double rate;
double fc;
double Q;
double gain;
int nstages;
double a0, a1, a2, b1, b2;
std::vector<double> x0, x1, x2, y0, y1, y2;
DBQLP(
int run,
int size,
float* in,
float* out,
double rate,
double fc,
double Q,
double gain,
int nstages
);
DBQLP(const DBQLP&) = delete;
DBQLP& operator=(DBQLP& other) = delete;
~DBQLP() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
void calc();
};
} // namespace WDSP
#endif

View File

@ -31,81 +31,84 @@ warren@wpratt.com
namespace WDSP {
DELAY* create_delay (int run, int size, float* in, float* out, int rate, float tdelta, float tdelay)
DELAY::DELAY(
int _run,
int _size,
float* _in,
float* _out,
int _rate,
float _tdelta,
float _tdelay
)
{
DELAY *a = new DELAY;
a->run = run;
a->size = size;
a->in = in;
a->out = out;
a->rate = rate;
a->tdelta = tdelta;
a->tdelay = tdelay;
a->L = (int)(0.5 + 1.0 / (a->tdelta * (float)a->rate));
a->adelta = 1.0 / (a->rate * a->L);
a->ft = 0.45 / (float)a->L;
a->ncoef = (int)(60.0 / a->ft);
a->ncoef = (a->ncoef / a->L + 1) * a->L;
a->cpp = a->ncoef / a->L;
a->phnum = (int)(0.5 + a->tdelay / a->adelta);
a->snum = a->phnum / a->L;
a->phnum %= a->L;
a->idx_in = 0;
a->adelay = a->adelta * (a->snum * a->L + a->phnum);
a->h = FIR::fir_bandpass (a->ncoef,-a->ft, +a->ft, 1.0, 1, 0, (float)a->L);
a->rsize = a->cpp + (WSDEL - 1);
a->ring = new float[a->rsize * 2]; // (float *) malloc0 (a->rsize * sizeof (complex));
return a;
run = _run;
size = _size;
in = _in;
out = _out;
rate = _rate;
tdelta = _tdelta;
tdelay = _tdelay;
L = (int)(0.5 + 1.0 / (tdelta * (float)rate));
adelta = 1.0f / (float) (rate * L);
ft = 0.45f / (float)L;
ncoef = (int)(60.0 / ft);
ncoef = (ncoef / L + 1) * L;
cpp = ncoef / L;
phnum = (int)(0.5 + tdelay / adelta);
snum = phnum / L;
phnum %= L;
idx_in = 0;
adelay = adelta * (float) (snum * L + phnum);
FIR::fir_bandpass (h, ncoef,-ft, +ft, 1.0, 1, 0, (float)L);
rsize = cpp + (WSDEL - 1);
ring.resize(rsize * 2);
}
void DELAY::destroy_delay (DELAY *a)
void DELAY::flush()
{
delete[] (a->ring);
delete[] (a->h);
delete (a);
std::fill(ring.begin(), ring.end(), 0);
idx_in = 0;
}
void DELAY::flush_delay (DELAY *a)
void DELAY::execute()
{
std::fill(a->ring, a->ring + a->cpp * 2, 0);
a->idx_in = 0;
}
void DELAY::xdelay (DELAY *a)
{
if (a->run)
if (run)
{
int i, j, k, idx, n;
float Itmp, Qtmp;
int j;
int k;
int idx;
int n;
float Itmp;
float Qtmp;
for (i = 0; i < a->size; i++)
for (int i = 0; i < size; i++)
{
a->ring[2 * a->idx_in + 0] = a->in[2 * i + 0];
a->ring[2 * a->idx_in + 1] = a->in[2 * i + 1];
ring[2 * idx_in + 0] = in[2 * i + 0];
ring[2 * idx_in + 1] = in[2 * i + 1];
Itmp = 0.0;
Qtmp = 0.0;
if ((n = a->idx_in + a->snum) >= a->rsize)
n -= a->rsize;
if ((n = idx_in + snum) >= rsize)
n -= rsize;
for (j = 0, k = a->L - 1 - a->phnum; j < a->cpp; j++, k+= a->L)
for (j = 0, k = L - 1 - phnum; j < cpp; j++, k+= L)
{
if ((idx = n + j) >= a->rsize)
idx -= a->rsize;
if ((idx = n + j) >= rsize)
idx -= rsize;
Itmp += a->ring[2 * idx + 0] * a->h[k];
Qtmp += a->ring[2 * idx + 1] * a->h[k];
Itmp += ring[2 * idx + 0] * h[k];
Qtmp += ring[2 * idx + 1] * h[k];
}
a->out[2 * i + 0] = Itmp;
a->out[2 * i + 1] = Qtmp;
out[2 * i + 0] = Itmp;
out[2 * i + 1] = Qtmp;
if (--a->idx_in < 0)
a->idx_in = a->rsize - 1;
if (--idx_in < 0)
idx_in = rsize - 1;
}
}
else if (a->out != a->in)
std::copy( a->in, a->in + a->size * 2, a->out);
else if (out != in)
std::copy( in, in + size * 2, out);
}
/********************************************************************************************************
@ -114,28 +117,28 @@ void DELAY::xdelay (DELAY *a)
* *
********************************************************************************************************/
void DELAY::SetDelayRun (DELAY *a, int run)
void DELAY::setRun(int _run)
{
a->run = run;
run = _run;
}
float DELAY::SetDelayValue (DELAY *a, float tdelay)
float DELAY::setValue(float _tdelay)
{
float adelay;
a->tdelay = tdelay;
a->phnum = (int)(0.5 + a->tdelay / a->adelta);
a->snum = a->phnum / a->L;
a->phnum %= a->L;
a->adelay = a->adelta * (a->snum * a->L + a->phnum);
adelay = a->adelay;
float _adelay;
tdelay = _tdelay;
phnum = (int)(0.5 + tdelay / adelta);
snum = phnum / L;
phnum %= L;
_adelay = adelta * (float) (snum * L + phnum);
adelay = _adelay;
return adelay;
}
void DELAY::SetDelayBuffs (DELAY *a, int size, float* in, float* out)
void DELAY::setBuffs(int _size, float* _in, float* _out)
{
a->size = size;
a->in = in;
a->out = out;
size = _size;
in = _in;
out = _out;
}
} // namespace WDSP

View File

@ -28,6 +28,8 @@ warren@wpratt.com
#ifndef wdsp_delay_h
#define wdsp_delay_h
#include <vector>
#include "export.h"
#define WSDEL 1025 // number of supported whole sample delays
@ -49,25 +51,36 @@ public:
int ncoef; // number of coefficients
int cpp; // coefficients per phase
float ft; // normalized cutoff frequency
float* h; // coefficients
std::vector<float> h; // coefficients
int snum; // starting sample number (0 for sub-sample delay)
int phnum; // phase number
int idx_in; // index for input into ring
int rsize; // ring size in complex samples
float* ring; // ring buffer
std::vector<float> ring; // ring buffer
float adelta; // actual delay increment
float adelay; // actual delay
static DELAY* create_delay (int run, int size, float* in, float* out, int rate, float tdelta, float tdelay);
static void destroy_delay (DELAY *a);
static void flush_delay (DELAY *a);
static void xdelay (DELAY *a);
DELAY(
int run,
int size,
float* in,
float* out,
int rate,
float tdelta,
float tdelay
);
DELAY(const DELAY&) = delete;
DELAY& operator=(DELAY& other) = delete;
~DELAY() = default;
void flush();
void execute();
// Properties
static void SetDelayRun (DELAY *a, int run);
static float SetDelayValue (DELAY *a, float delay); // returns actual delay in seconds
static void SetDelayBuffs (DELAY *a, int size, float* in, float* out);
void setRun(int run);
float setValue(float delay); // returns actual delay in seconds
void setBuffs(int size, float* in, float* out);
};
} // namespace WDSP

126
wdsp/dsphp.cpp Normal file
View File

@ -0,0 +1,126 @@
/* iir.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "dsphp.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Double Single-Pole High-Pass *
* *
********************************************************************************************************/
void DSPHP::calc()
{
double g;
x0.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
x1.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y0.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
y1.resize(nstages); // (float*)malloc0(nstages * sizeof(float));
g = exp(-TWOPI * fc / rate);
b0 = +0.5 * (1.0 + g);
b1 = -0.5 * (1.0 + g);
a1 = -g;
}
DSPHP::DSPHP(
int _run,
int _size,
float* _in,
float* _out,
double _rate,
double _fc,
int _nstages
) :
run(_run),
size(_size),
in(_in),
out(_out),
rate(_rate),
fc(_fc),
nstages(_nstages)
{
calc();
}
void DSPHP::flush()
{
std::fill(x0.begin(), x0.end(), 0);
std::fill(x1.begin(), x1.end(), 0);
std::fill(y0.begin(), y0.end(), 0);
std::fill(y1.begin(), y1.end(), 0);
}
void DSPHP::execute()
{
if (run)
{
for (int i = 0; i < size; i++)
{
x0[0] = in[i];
for (int n = 0; n < nstages; n++)
{
if (n > 0)
x0[n] = y0[n - 1];
y0[n] = b0 * x0[n]
+ b1 * x1[n]
- a1 * y1[n];
y1[n] = y0[n];
x1[n] = x0[n];
}
out[i] = (float) y0[nstages - 1];
}
}
else if (out != in)
{
std::copy(in, in + size, out);
}
}
void DSPHP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
}
void DSPHP::setSamplerate(int _rate)
{
rate = _rate;
calc();
}
void DSPHP::setSize(int _size)
{
size = _size;
}
} // namespace WDSP

90
wdsp/dsphp.hpp Normal file
View File

@ -0,0 +1,90 @@
/* sphp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Double Single-Pole High-Pass *
* *
********************************************************************************************************/
#ifndef wdsp_dsphp_h
#define wdsp_dsphp_h
#include <vector>
#include "export.h"
namespace WDSP {
class WDSP_API DSPHP
{
public:
int run;
int size;
float* in;
float* out;
double rate;
double fc;
int nstages;
double a1;
double b0;
double b1;
std::vector<double> x0;
std::vector<double> x1;
std::vector<double> y0;
std::vector<double> y1;
DSPHP(
int run,
int size,
float* in,
float* out,
double rate,
double fc,
int nstages
);
DSPHP(const DSPHP&) = delete;
DSPHP& operator=(DSPHP& other) = delete;
~DSPHP() = default;
void destroy();
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
void calc();
void decalc();
};
} // namespace WDSP
#endif

File diff suppressed because it is too large Load Diff

View File

@ -28,13 +28,14 @@ warren@wpratt.com
#ifndef wdsp_emnr_h
#define wdsp_emnr_h
#include <array>
#include <vector>
#include "fftw3.h"
#include "export.h"
namespace WDSP {
class RXA;
class WDSP_API EMNR
{
public:
@ -46,18 +47,18 @@ public:
int fsize;
int ovrlp;
int incr;
float* window;
std::vector<float> window;
int iasize;
float* inaccum;
float* forfftin;
float* forfftout;
std::vector<float> inaccum;
std::vector<float> forfftin;
std::vector<float> forfftout;
int msize;
double* mask;
float* revfftin;
float* revfftout;
float** save;
std::vector<double> mask;
std::vector<float> revfftin;
std::vector<float> revfftout;
std::vector<std::vector<float>> save;
int oasize;
float* outaccum;
std::vector<float> outaccum;
double rate;
int wintype;
double ogain;
@ -71,18 +72,21 @@ public:
int saveidx;
fftwf_plan Rfor;
fftwf_plan Rrev;
struct _g
struct G
{
int incr;
double rate;
int msize;
std::vector<double>& mask;
const std::vector<float>& y;
int gain_method;
int npe_method;
int ae_run;
double msize;
double* mask;
float* y;
double* lambda_y;
double* lambda_d;
double* prev_mask;
double* prev_gamma;
std::vector<double> lambda_y;
std::vector<double> lambda_d;
std::vector<double> prev_mask;
std::vector<double> prev_gamma;
double gf1p5;
double alpha;
double eps_floor;
@ -90,80 +94,158 @@ public:
double q;
double gmax;
//
double* GG;
double* GGS;
std::array<double, 241*241> GG;
std::array<double, 241*241> GGS;
FILE* fileb;
} g;
struct _npest
G(
int incr,
double rate,
int msize,
std::vector<double>& mask,
const std::vector<float>& y
);
G(const G&) = delete;
G& operator=(const G& other) = delete;
~G() = default;
void calc_gamma0();
void calc_gamma1();
void calc_gamma2();
void calc_lambda_y();
private:
static double getKey(const std::array<double, 241*241>& type, double gamma, double xi);
static double e1xb (double x);
static double bessI0 (double x);
static double bessI1 (double x);
};
G *g;
struct NP
{
int incr;
double rate;
int msize;
double* lambda_y;
double* lambda_d;
double* p;
double* alphaOptHat;
double alphaC;
std::vector<double>& lambda_y;
std::vector<double>& lambda_d;
double alphaCsmooth;
double alphaCmin;
double* alphaHat;
double alphaMax;
double* sigma2N;
double alphaCmin;
double alphaMin_max_value;
double snrq;
double betamax;
double* pbar;
double* p2bar;
double invQeqMax;
double av;
double* Qeq;
int U;
double Dtime;
int U;
int V;
int D;
std::vector<double> p;
std::vector<double> alphaOptHat;
double alphaC;
std::vector<double> alphaHat;
std::vector<double> sigma2N;
std::vector<double> pbar;
std::vector<double> p2bar;
std::vector<double> Qeq;
double MofD;
double MofV;
double* bmin;
double* bmin_sub;
int* k_mod;
double* actmin;
double* actmin_sub;
std::array<double, 4> invQbar_points;
std::array<double, 4> nsmax;
std::vector<double> bmin;
std::vector<double> bmin_sub;
std::vector<int> k_mod;
std::vector<double> actmin;
std::vector<double> actmin_sub;
int subwc;
int* lmin_flag;
double* pmin_u;
double invQbar_points[4];
double nsmax[4];
double** actminbuff;
std::vector<int> lmin_flag;
std::vector<double> pmin_u;
std::vector<std::vector<double>> actminbuff;
int amb_idx;
} np;
struct _npests
NP(
int incr,
double rate,
int msize,
std::vector<double>& lambda_y,
std::vector<double>& lambda_d
);
NP(const NP&) = delete;
NP& operator=(const NP& other) = delete;
~NP() = default;
void LambdaD();
private:
static const std::array<double, 18> DVals;
static const std::array<double, 18> MVals;
static void interpM (
double* res,
double x,
int nvals,
const std::array<double, 18>& xvals,
const std::array<double, 18>& yvals
);
};
NP *np;
struct NPS
{
int incr;
double rate;
int msize;
double* lambda_y;
double* lambda_d;
const std::vector<double>& lambda_y;
std::vector<double>& lambda_d;
double alpha_pow;
double alpha_Pbar;
double epsH1;
double epsH1r;
double* sigma2N;
double* PH1y;
double* Pbar;
double* EN2y;
} nps;
struct _ae
std::vector<double> sigma2N;
std::vector<double> PH1y;
std::vector<double> Pbar;
std::vector<double> EN2y;
NPS(
int incr,
double rate,
int msize,
const std::vector<double>& lambda_y,
std::vector<double>& lambda_d,
double alpha_pow,
double alpha_Pbar,
double epsH1
);
NPS(const NPS&) = delete;
NPS& operator=(const NPS& other) = delete;
~NPS() = default;
void LambdaDs();
};
NPS *nps;
struct AE
{
int msize;
double* lambda_y;
const std::vector<double>& lambda_y;
double zetaThresh;
double psi;
double* nmask;
} ae;
std::vector<double> nmask;
static EMNR* create_emnr (
AE(
int msize,
const std::vector<double>& lambda_y,
double zetaThresh,
double psi
);
AE(const AE&) = delete;
AE& operator=(const AE& other) = delete;
~AE() = default;
};
AE *ae;
EMNR(
int run,
int position,
int size,
@ -178,34 +260,28 @@ public:
int npe_method,
int ae_run
);
static void destroy_emnr (EMNR *a);
static void flush_emnr (EMNR *a);
static void xemnr (EMNR *a, int pos);
static void setBuffers_emnr (EMNR *a, float* in, float* out);
static void setSamplerate_emnr (EMNR *a, int rate);
static void setSize_emnr (EMNR *a, int size);
// RXA Properties
static void SetEMNRRun (RXA& rxa, int run);
static void SetEMNRgainMethod (RXA& rxa, int method);
static void SetEMNRnpeMethod (RXA& rxa, int method);
static void SetEMNRaeRun (RXA& rxa, int run);
static void SetEMNRPosition (RXA& rxa, int position);
static void SetEMNRaeZetaThresh (RXA& rxa, double zetathresh);
static void SetEMNRaePsi (RXA& rxa, double psi);
EMNR(const EMNR&) = delete;
EMNR& operator=(const EMNR& other) = delete;
~EMNR();
void flush();
void execute(int pos);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// Public Properties
void setGainMethod(int method);
void setNpeMethod(int method);
void setAeRun(int run);
void setAeZetaThresh(double zetathresh);
void setAePsi(double psi);
private:
static double bessI0 (double x);
static double bessI1 (double x);
static double e1xb (double x);
static void calc_window (EMNR *a);
static void interpM (double* res, double x, int nvals, double* xvals, double* yvals);
static void calc_emnr(EMNR *a);
static void decalc_emnr(EMNR *a);
static void LambdaD(EMNR *a);
static void LambdaDs (EMNR *a);
static void aepf(EMNR *a);
static double getKey(double* type, double gamma, double xi);
static void calc_gain (EMNR *a);
void calc_window();
void calc();
void decalc();
void aepf();
void calc_gain();
};
} // namespace WDSP

View File

@ -33,239 +33,127 @@ warren@wpratt.com
namespace WDSP {
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
EMPHP* EMPHP::create_emphp (int run, int position, int size, int nc, int mp, float* in, float* out, int rate, int ctype, float f_low, float f_high)
{
EMPHP *a = new EMPHP;
float* impulse;
a->run = run;
a->position = position;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->rate = rate;
a->ctype = ctype;
a->f_low = f_low;
a->f_high = f_high;
impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
delete[] (impulse);
return a;
}
void EMPHP::destroy_emphp (EMPHP *a)
{
FIRCORE::destroy_fircore (a->p);
delete (a);
}
void EMPHP::flush_emphp (EMPHP *a)
{
FIRCORE::flush_fircore (a->p);
}
void EMPHP::xemphp (EMPHP *a, int position)
{
if (a->run && a->position == position)
FIRCORE::xfircore (a->p);
else if (a->in != a->out)
std::copy( a->in, a->in + a->size * 2, a->out);
}
void EMPHP::setBuffers_emphp (EMPHP *a, float* in, float* out)
{
a->in = in;
a->out = out;
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
}
void EMPHP::setSamplerate_emphp (EMPHP *a, int rate)
{
float* impulse;
a->rate = rate;
impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EMPHP::setSize_emphp (EMPHP *a, int size)
{
float* impulse;
a->size = size;
FIRCORE::setSize_fircore (a->p, a->size);
impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis: TXA Properties *
* *
********************************************************************************************************/
void EMPHP::SetFMEmphPosition (TXA& txa, int position)
{
txa.preemph.p->position = position;
}
void EMPHP::SetFMEmphMP (TXA& txa, int mp)
{
EMPHP *a;
a = txa.preemph.p;
if (a->mp != mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
}
}
void EMPHP::SetFMEmphNC (TXA& txa, int nc)
{
EMPHP *a;
float* impulse;
a = txa.preemph.p;
if (a->nc != nc)
{
a->nc = nc;
impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
}
}
void EMPHP::SetFMPreEmphFreqs (TXA& txa, float low, float high)
{
EMPHP *a;
float* impulse;
a = txa.preemph.p;
if (a->f_low != low || a->f_high != high)
{
a->f_low = low;
a->f_high = high;
impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
}
/********************************************************************************************************
* *
* Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
void EMPH::calc_emph (EMPH *a)
void EMPH::calc()
{
a->infilt = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex));
a->product = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex));
a->mults = FCurve::fc_mults(a->size, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0);
a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
infilt.resize(2 * size * 2);
product.resize(2 * size * 2);
FCurve::fc_mults(
mults,
size,
(float) f_low,
(float) f_high,
(float) (-20.0 * log10(f_high / f_low)),
0.0,
ctype,
(float) rate,
(float) (1.0 / (2.0 * size)),
0,
0
);
CFor = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *)infilt.data(),
(fftwf_complex *)product.data(),
FFTW_FORWARD,
FFTW_PATIENT)
;
CRev = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *)product.data(),
(fftwf_complex *)out,
FFTW_BACKWARD,
FFTW_PATIENT
);
}
void EMPH::decalc_emph (EMPH *a)
void EMPH::decalc()
{
fftwf_destroy_plan(a->CRev);
fftwf_destroy_plan(a->CFor);
delete[] (a->mults);
delete[] (a->product);
delete[] (a->infilt);
fftwf_destroy_plan(CRev);
fftwf_destroy_plan(CFor);
}
EMPH* EMPH::create_emph (int run, int position, int size, float* in, float* out, int rate, int ctype, float f_low, float f_high)
EMPH::EMPH(
int _run,
int _position,
int _size,
float* _in,
float* _out,
int _rate,
int _ctype,
double _f_low,
double _f_high
) :
run(_run),
position(_position),
size(_size),
in(_in),
out(_out),
ctype(_ctype),
f_low(_f_low),
f_high(_f_high),
rate((double) _rate)
{
EMPH *a = new EMPH;
a->run = run;
a->position = position;
a->size = size;
a->in = in;
a->out = out;
a->rate = (float)rate;
a->ctype = ctype;
a->f_low = f_low;
a->f_high = f_high;
calc_emph (a);
return a;
calc();
}
void EMPH::destroy_emph (EMPH *a)
EMPH::~EMPH()
{
decalc_emph (a);
delete (a);
decalc();
}
void EMPH::flush_emph (EMPH *a)
void EMPH::flush()
{
std::fill(a->infilt, a->infilt + 2 * a->size * 2, 0);
std::fill(infilt.begin(), infilt.end(), 0);
}
void EMPH::xemph (EMPH *a, int position)
void EMPH::execute(int _position)
{
int i;
float I, Q;
if (a->run && a->position == position)
double I;
double Q;
if (run && position == _position)
{
std::copy(a->in, a->in + a->size * 2, &(a->infilt[2 * a->size]));
fftwf_execute (a->CFor);
for (i = 0; i < 2 * a->size; i++)
std::copy(in, in + size * 2, &(infilt[2 * size]));
fftwf_execute (CFor);
for (int i = 0; i < 2 * size; i++)
{
I = a->product[2 * i + 0];
Q = a->product[2 * i + 1];
a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1];
a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0];
I = product[2 * i + 0];
Q = product[2 * i + 1];
product[2 * i + 0] = (float) (I * mults[2 * i + 0] - Q * mults[2 * i + 1]);
product[2 * i + 1] = (float) (I * mults[2 * i + 1] + Q * mults[2 * i + 0]);
}
fftwf_execute (a->CRev);
std::copy(&(a->infilt[2 * a->size]), &(a->infilt[2 * a->size]) + a->size * 2, a->infilt);
fftwf_execute (CRev);
std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt.begin());
}
else if (a->in != a->out)
std::copy( a->in, a->in + a->size * 2, a->out);
else if (in != out)
std::copy( in, in + size * 2, out);
}
void EMPH::setBuffers_emph (EMPH *a, float* in, float* out)
void EMPH::setBuffers(float* _in, float* _out)
{
decalc_emph (a);
a->in = in;
a->out = out;
calc_emph (a);
decalc();
in = _in;
out = _out;
calc();
}
void EMPH::setSamplerate_emph (EMPH *a, int rate)
void EMPH::setSamplerate(int _rate)
{
decalc_emph (a);
a->rate = rate;
calc_emph (a);
decalc();
rate = _rate;
calc();
}
void EMPH::setSize_emph (EMPH *a, int size)
void EMPH::setSize(int _size)
{
decalc_emph(a);
a->size = size;
calc_emph(a);
decalc();
size = _size;
calc();
}
/********************************************************************************************************
* *
* Overlap-Save FM Pre-Emphasis: TXA Properties *
* *
********************************************************************************************************/
/* // Uncomment when needed
PORT
void SetTXAFMEmphPosition (int channel, int position)
{
ch.csDSP.lock();
txa.preemph.p->position = position;
ch.csDSP.unlock();
}
*/
} // namespace WDSP

View File

@ -25,57 +25,6 @@ warren@wpratt.com
*/
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
#ifndef wdsp_emphp_h
#define wdsp_emphp_h
#include "export.h"
namespace WDSP {
class FIRCORE;
class TXA;
class WDSP_API EMPHP
{
public:
int run;
int position;
int size;
int nc;
int mp;
float* in;
float* out;
int ctype;
float f_low;
float f_high;
float rate;
FIRCORE *p;
static EMPHP* create_emphp (int run, int position, int size, int nc, int mp,
float* in, float* out, int rate, int ctype, float f_low, float f_high);
static void destroy_emphp (EMPHP *a);
static void flush_emphp (EMPHP *a);
static void xemphp (EMPHP *a, int position);
static void setBuffers_emphp (EMPHP *a, float* in, float* out);
static void setSamplerate_emphp (EMPHP *a, int rate);
static void setSize_emphp (EMPHP *a, int size);
// TXA Properties
static void SetFMEmphPosition (TXA& txa, int position);
static void SetFMEmphMP (TXA& txa, int mp);
static void SetFMEmphNC (TXA& txa, int nc);
static void SetFMPreEmphFreqs(TXA& txa, float low, float high);
};
} // namespace WDSP
#endif
/********************************************************************************************************
* *
* Overlap-Save FM Pre-Emphasis *
@ -85,6 +34,8 @@ public:
#ifndef _emph_h
#define _emph_h
#include <vector>
#include "fftw3.h"
#include "export.h"
@ -92,32 +43,46 @@ namespace WDSP {
class WDSP_API EMPH
{
public:
int run;
int position;
int size;
float* in;
float* out;
int ctype;
float f_low;
float f_high;
float* infilt;
float* product;
float* mults;
float rate;
double f_low;
double f_high;
std::vector<float> infilt;
std::vector<float> product;
std::vector<float> mults;
double rate;
fftwf_plan CFor;
fftwf_plan CRev;
static EMPH* create_emph (int run, int position, int size, float* in, float* out, int rate, int ctype, float f_low, float f_high);
static void destroy_emph (EMPH *a);
static void flush_emph (EMPH *a);
static void xemph (EMPH *a, int position);
static void setBuffers_emph (EMPH *a, float* in, float* out);
static void setSamplerate_emph (EMPH *a, int rate);
static void setSize_emph (EMPH *a, int size);
EMPH(
int run,
int position,
int size,
float* in,
float* out,
int rate,
int ctype,
double f_low,
double f_high
);
EMPH(const EMPH&) = delete;
EMPH& operator=(const EMPH& other) = delete;
~EMPH();
void flush();
void execute(int position);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
static void calc_emph (EMPH *a);
static void decalc_emph (EMPH *a);
void calc();
void decalc();
};
} // namespace WDSP

215
wdsp/emphp.cpp Normal file
View File

@ -0,0 +1,215 @@
/* emph.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2016, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "emphp.hpp"
#include "fcurve.hpp"
#include "fircore.hpp"
#include "TXA.hpp"
namespace WDSP {
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
EMPHP::EMPHP(
int _run,
int _position,
int _size,
int _nc,
int _mp,
float* _in,
float* _out,
int _rate,
int _ctype,
double _f_low,
double _f_high
)
{
run = _run;
position = _position;
size = _size;
nc = _nc;
mp = _mp;
in = _in;
out = _out;
rate = _rate;
ctype = _ctype;
f_low = _f_low;
f_high = _f_high;
std::vector<float> impulse(2 * nc);
FCurve::fc_impulse (
impulse,
nc,
(float) f_low,
(float) f_high,
(float) (-20.0 * log10(f_high / f_low)),
0.0,
ctype,
(float) rate,
(float) (1.0 / (2.0 * size)),
0, 0
);
p = new FIRCORE(size, in, out, mp, impulse);
}
EMPHP::~EMPHP()
{
delete p;
}
void EMPHP::flush()
{
p->flush();
}
void EMPHP::execute(int _position)
{
if (run && position == _position)
p->execute();
else if (in != out)
std::copy( in, in + size * 2, out);
}
void EMPHP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
p->setBuffers(in, out);
}
void EMPHP::setSamplerate(int _rate)
{
rate = _rate;
std::vector<float> impulse(2 * nc);
FCurve::fc_impulse (
impulse,
nc,
(float) f_low,
(float) f_high,
(float) (-20.0 * log10(f_high / f_low)),
0.0,
ctype,
(float) rate,
(float) (1.0 / (2.0 * size)),
0, 0
);
p->setImpulse(impulse, 1);
}
void EMPHP::setSize(int _size)
{
size = _size;
p->setSize(size);
std::vector<float> impulse(2 * nc);
FCurve::fc_impulse (
impulse,
nc,
(float) f_low,
(float) f_high,
(float) (-20.0 * log10(f_high / f_low)),
0.0,
ctype,
(float) rate,
(float) (1.0 / (2.0 * size)),
0,
0
);
p->setImpulse(impulse, 1);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis: TXA Properties *
* *
********************************************************************************************************/
void EMPHP::setPosition(int _position)
{
position = _position;
}
void EMPHP::setMP(int _mp)
{
if (mp != _mp)
{
mp = _mp;
p->setMp(mp);
}
}
void EMPHP::setNC(int _nc)
{
if (nc != _nc)
{
nc = _nc;
std::vector<float> impulse(2 * nc);
FCurve::fc_impulse (
impulse,
nc,
(float) f_low,
(float) f_high,
(float) (-20.0 * log10(f_high / f_low)),
0.0,
ctype,
(float) rate,
(float) (1.0 / (2.0 * size)),
0,
0
);
p->setNc(impulse);
}
}
void EMPHP::setFreqs(double low, double high)
{
if (f_low != low || f_high != high)
{
f_low = low;
f_high = high;
std::vector<float> impulse(2 * nc);
FCurve::fc_impulse (
impulse,
nc,
(float) f_low,
(float) f_high,
(float) (-20.0 * log10(f_high / f_low)),
0.0,
ctype,
(float) rate,
(float) (1.0 / (2.0 * size)),
0,
0
);
p->setImpulse(impulse, 1);
}
}
} // namespace WDSP

91
wdsp/emphp.hpp Normal file
View File

@ -0,0 +1,91 @@
/* emph.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2014, 2016, 2023 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Partitioned Overlap-Save FM Pre-Emphasis *
* *
********************************************************************************************************/
#ifndef wdsp_emphp_h
#define wdsp_emphp_h
#include "export.h"
namespace WDSP {
class FIRCORE;
class TXA;
class WDSP_API EMPHP
{
public:
int run;
int position;
int size;
int nc;
int mp;
float* in;
float* out;
int ctype;
double f_low;
double f_high;
double rate;
FIRCORE *p;
EMPHP(
int run,
int position,
int size,
int nc,
int mp,
float* in,
float* out,
int rate,
int ctype,
double f_low,
double f_high
);
EMPHP(const EMPHP&) = delete;
EMPHP& operator=(const EMPHP& other) = delete;
~EMPHP();
void flush();
void execute(int position);
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// TXA Properties
void setPosition(int position);
void setMP(int mp);
void setNC(int nc);
void setFreqs(double low, double high);
};
} // namespace WDSP
#endif

View File

@ -27,6 +27,7 @@ warren@wpratt.com
#include "comm.hpp"
#include "eq.hpp"
#include "eqp.hpp"
#include "fircore.hpp"
#include "fir.hpp"
#include "RXA.hpp"
@ -34,515 +35,6 @@ warren@wpratt.com
namespace WDSP {
int EQP::fEQcompare (const void * a, const void * b)
{
if (*(float*)a < *(float*)b)
return -1;
else if (*(float*)a == *(float*)b)
return 0;
else
return 1;
}
float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype)
{
float* fp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float));
float* gp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float));
float* A = new float[N / 2 + 1]; // (float *) malloc0 ((N / 2 + 1) * sizeof (float));
float* sary = new float[2 * nfreqs]; // (float *) malloc0 (2 * nfreqs * sizeof (float));
double gpreamp, f, frac;
float* impulse;
int i, j, mid;
fp[0] = 0.0;
fp[nfreqs + 1] = 1.0;
gpreamp = G[0];
for (i = 1; i <= nfreqs; i++)
{
fp[i] = 2.0 * F[i] / samplerate;
if (fp[i] < 0.0)
fp[i] = 0.0;
if (fp[i] > 1.0)
fp[i] = 1.0;
gp[i] = G[i];
}
for (i = 1, j = 0; i <= nfreqs; i++, j+=2)
{
sary[j + 0] = fp[i];
sary[j + 1] = gp[i];
}
qsort (sary, nfreqs, 2 * sizeof (float), fEQcompare);
for (i = 1, j = 0; i <= nfreqs; i++, j+=2)
{
fp[i] = sary[j + 0];
gp[i] = sary[j + 1];
}
gp[0] = gp[1];
gp[nfreqs + 1] = gp[nfreqs];
mid = N / 2;
j = 0;
if (N & 1)
{
for (i = 0; i <= mid; i++)
{
f = (double)i / (double)mid;
while ((f > fp[j + 1]) && (j < nfreqs))
j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale;
}
}
else
{
for (i = 0; i < mid; i++)
{
f = ((double)i + 0.5) / (double)mid;
while ((f > fp[j + 1]) && (j < nfreqs))
j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale;
}
}
if (ctfmode == 0)
{
int k, low, high;
double lowmag, highmag, flow4, fhigh4;
if (N & 1)
{
low = (int)(fp[1] * mid);
high = (int)(fp[nfreqs] * mid + 0.5);
lowmag = A[low];
highmag = A[high];
flow4 = pow((double)low / (double)mid, 4.0);
fhigh4 = pow((double)high / (double)mid, 4.0);
k = low;
while (--k >= 0)
{
f = (double)k / (double)mid;
lowmag *= (f * f * f * f) / flow4;
if (lowmag < 1.0e-20) lowmag = 1.0e-20;
A[k] = lowmag;
}
k = high;
while (++k <= mid)
{
f = (double)k / (double)mid;
highmag *= fhigh4 / (f * f * f * f);
if (highmag < 1.0e-20) highmag = 1.0e-20;
A[k] = highmag;
}
}
else
{
low = (int)(fp[1] * mid - 0.5);
high = (int)(fp[nfreqs] * mid - 0.5);
lowmag = A[low];
highmag = A[high];
flow4 = pow((double)low / (double)mid, 4.0);
fhigh4 = pow((double)high / (double)mid, 4.0);
k = low;
while (--k >= 0)
{
f = (double)k / (double)mid;
lowmag *= (f * f * f * f) / flow4;
if (lowmag < 1.0e-20) lowmag = 1.0e-20;
A[k] = lowmag;
}
k = high;
while (++k < mid)
{
f = (double)k / (double)mid;
highmag *= fhigh4 / (f * f * f * f);
if (highmag < 1.0e-20) highmag = 1.0e-20;
A[k] = highmag;
}
}
}
if (N & 1)
impulse = FIR::fir_fsamp_odd(N, A, 1, 1.0, wintype);
else
impulse = FIR::fir_fsamp(N, A, 1, 1.0, wintype);
// print_impulse("eq.txt", N, impulse, 1, 0);
delete[] (sary);
delete[] (A);
delete[] (gp);
delete[] (fp);
return impulse;
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer *
* *
********************************************************************************************************/
EQP* EQP::create_eqp (
int run,
int size,
int nc,
int mp,
float *in,
float *out,
int nfreqs,
float* F,
float* G,
int ctfmode,
int wintype,
int samplerate
)
{
// NOTE: 'nc' must be >= 'size'
EQP *a = new EQP;
float* impulse;
a->run = run;
a->size = size;
a->nc = nc;
a->mp = mp;
a->in = in;
a->out = out;
a->nfreqs = nfreqs;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
memcpy (a->F, F, (nfreqs + 1) * sizeof (float));
memcpy (a->G, G, (nfreqs + 1) * sizeof (float));
a->ctfmode = ctfmode;
a->wintype = wintype;
a->samplerate = (float)samplerate;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
delete[] (impulse);
return a;
}
void EQP::destroy_eqp (EQP *a)
{
FIRCORE::destroy_fircore (a->p);
delete (a);
}
void EQP::flush_eqp (EQP *a)
{
FIRCORE::flush_fircore (a->p);
}
void EQP::xeqp (EQP *a)
{
if (a->run)
FIRCORE::xfircore (a->p);
else
std::copy( a->in, a->in + a->size * 2, a->out);
}
void EQP::setBuffers_eqp (EQP *a, float* in, float* out)
{
a->in = in;
a->out = out;
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
}
void EQP::setSamplerate_eqp (EQP *a, int rate)
{
float* impulse;
a->samplerate = rate;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::setSize_eqp (EQP *a, int size)
{
float* impulse;
a->size = size;
FIRCORE::setSize_fircore (a->p, a->size);
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer: RXA Properties *
* *
********************************************************************************************************/
void EQP::SetEQRun (RXA& rxa, int run)
{
rxa.eqp.p->run = run;
}
void EQP::SetEQNC (RXA& rxa, int nc)
{
EQP *a;
float* impulse;
a = rxa.eqp.p;
if (a->nc != nc)
{
a->nc = nc;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
}
}
void EQP::SetEQMP (RXA& rxa, int mp)
{
EQP *a;
a = rxa.eqp.p;
if (a->mp != mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
}
}
void EQP::SetEQProfile (RXA& rxa, int nfreqs, const float* F, const float* G)
{
EQP *a;
float* impulse;
a = rxa.eqp.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = nfreqs;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
memcpy (a->F, F, (nfreqs + 1) * sizeof (float));
memcpy (a->G, G, (nfreqs + 1) * sizeof (float));
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G,
a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetEQCtfmode (RXA& rxa, int mode)
{
EQP *a;
float* impulse;
a = rxa.eqp.p;
a->ctfmode = mode;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetEQWintype (RXA& rxa, int wintype)
{
EQP *a;
float* impulse;
a = rxa.eqp.p;
a->wintype = wintype;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetGrphEQ (RXA& rxa, int *rxeq)
{ // three band equalizer (legacy compatibility)
EQP *a;
float* impulse;
a = rxa.eqp.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 4;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (float)rxeq[0];
a->G[1] = (float)rxeq[1];
a->G[2] = (float)rxeq[1];
a->G[3] = (float)rxeq[2];
a->G[4] = (float)rxeq[3];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetGrphEQ10 (RXA& rxa, int *rxeq)
{ // ten band equalizer (legacy compatibility)
EQP *a;
float* impulse;
int i;
a = rxa.eqp.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 10;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (float)rxeq[i];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
// print_impulse ("rxeq.txt", a->nc, impulse, 1, 0);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer: TXA Properties *
* *
********************************************************************************************************/
void EQP::SetEQRun (TXA& txa, int run)
{
txa.eqp.p->run = run;
}
void EQP::SetEQNC (TXA& txa, int nc)
{
EQP *a;
float* impulse;
a = txa.eqp.p;
if (a->nc != nc)
{
a->nc = nc;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
delete[] (impulse);
}
}
void EQP::SetEQMP (TXA& txa, int mp)
{
EQP *a;
a = txa.eqp.p;
if (a->mp != mp)
{
a->mp = mp;
FIRCORE::setMp_fircore (a->p, a->mp);
}
}
void EQP::SetEQProfile (TXA& txa, int nfreqs, const float* F, const float* G)
{
EQP *a;
float* impulse;
a = txa.eqp.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = nfreqs;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
memcpy (a->F, F, (nfreqs + 1) * sizeof (float));
memcpy (a->G, G, (nfreqs + 1) * sizeof (float));
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetEQCtfmode (TXA& txa, int mode)
{
EQP *a;
float* impulse;
a = txa.eqp.p;
a->ctfmode = mode;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetEQWintype (TXA& txa, int wintype)
{
EQP *a;
float* impulse;
a = txa.eqp.p;
a->wintype = wintype;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetGrphEQ (TXA& txa, int *txeq)
{ // three band equalizer (legacy compatibility)
EQP *a;
float* impulse;
a = txa.eqp.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 4;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (float)txeq[0];
a->G[1] = (float)txeq[1];
a->G[2] = (float)txeq[1];
a->G[3] = (float)txeq[2];
a->G[4] = (float)txeq[3];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
void EQP::SetGrphEQ10 (TXA& txa, int *txeq)
{ // ten band equalizer (legacy compatibility)
EQP *a;
float* impulse;
int i;
a = txa.eqp.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 10;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (float)txeq[i];
a->ctfmode = 0;
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
delete[] (impulse);
}
/********************************************************************************************************
* *
* Overlap-Save Equalizer *
@ -550,332 +42,124 @@ void EQP::SetGrphEQ10 (TXA& txa, int *txeq)
********************************************************************************************************/
float* EQP::eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype)
void EQ::eq_mults (std::vector<float>& mults, int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype)
{
float* impulse = eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype);
float* mults = FIR::fftcv_mults(2 * size, impulse);
delete[] (impulse);
return mults;
std::vector<float> impulse;
EQP::eq_impulse (impulse, size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype);
std::vector<float> _mults;
FIR::fftcv_mults(_mults, 2 * size, impulse.data());
mults.resize(2 * size * 2);
std::copy(_mults.begin(), _mults.end(), mults.begin());
}
void EQ::calc_eq (EQ *a)
void EQ::calc()
{
a->scale = 1.0 / (float)(2 * a->size);
a->infilt = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex));
a->product = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex));
a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
a->mults = EQP::eq_mults(a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
scale = (float) (1.0 / (float)(2 * size));
infilt.resize(2 * size * 2);
product.resize(2 * size * 2);
CFor = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *) infilt.data(),
(fftwf_complex *) product.data(),
FFTW_FORWARD,
FFTW_PATIENT
);
CRev = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *) product.data(),
(fftwf_complex *) out,
FFTW_BACKWARD,
FFTW_PATIENT
);
EQ::eq_mults(mults, size, nfreqs, F.data(), G.data(), samplerate, scale, ctfmode, wintype);
}
void EQ::decalc_eq (EQ *a)
void EQ::decalc()
{
fftwf_destroy_plan(a->CRev);
fftwf_destroy_plan(a->CFor);
delete[] (a->mults);
delete[] (a->product);
delete[] (a->infilt);
fftwf_destroy_plan(CRev);
fftwf_destroy_plan(CFor);
}
EQ* EQ::create_eq (int run, int size, float *in, float *out, int nfreqs, float* F, float* G, int ctfmode, int wintype, int samplerate)
EQ::EQ(
int _run,
int _size,
float *_in,
float *_out,
int _nfreqs,
const float* _F,
const float* _G,
int _ctfmode,
int _wintype,
int _samplerate
) :
run(_run),
size(_size),
in(_in),
out(_out),
nfreqs(_nfreqs),
ctfmode(_ctfmode),
wintype(_wintype),
samplerate((float) _samplerate)
{
EQ *a = new EQ;
a->run = run;
a->size = size;
a->in = in;
a->out = out;
a->nfreqs = nfreqs;
a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
memcpy (a->F, F, (nfreqs + 1) * sizeof (float));
memcpy (a->G, G, (nfreqs + 1) * sizeof (float));
a->ctfmode = ctfmode;
a->wintype = wintype;
a->samplerate = (float)samplerate;
calc_eq (a);
return a;
F.resize(nfreqs + 1);
G.resize(nfreqs + 1);
std::copy(_F, _F + nfreqs + 1, F.begin());
std::copy(_G, _G + nfreqs + 1, G.begin());
calc();
}
void EQ::destroy_eq (EQ *a)
EQ::~EQ()
{
decalc_eq (a);
delete[] (a->G);
delete[] (a->F);
delete[] (a);
decalc();
}
void EQ::flush_eq (EQ *a)
void EQ::flush()
{
std::fill(a->infilt, a->infilt + 2 * a->size * 2, 0);
std::fill(infilt.begin(), infilt.end(), 0);
}
void EQ::xeq (EQ *a)
void EQ::execute()
{
int i;
float I, Q;
if (a->run)
float I;
float Q;
if (run)
{
std::copy(a->in, a->in + a->size * 2, &(a->infilt[2 * a->size]));
fftwf_execute (a->CFor);
for (i = 0; i < 2 * a->size; i++)
std::copy(in, in + size * 2, &(infilt[2 * size]));
fftwf_execute (CFor);
for (int i = 0; i < 2 * size; i++)
{
I = a->product[2 * i + 0];
Q = a->product[2 * i + 1];
a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1];
a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0];
I = product[2 * i + 0];
Q = product[2 * i + 1];
product[2 * i + 0] = I * mults[2 * i + 0] - Q * mults[2 * i + 1];
product[2 * i + 1] = I * mults[2 * i + 1] + Q * mults[2 * i + 0];
}
fftwf_execute (a->CRev);
std::copy(&(a->infilt[2 * a->size]), &(a->infilt[2 * a->size]) + a->size * 2, a->infilt);
fftwf_execute (CRev);
std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt);
}
else if (a->in != a->out)
std::copy( a->in, a->in + a->size * 2, a->out);
else if (in != out)
std::copy( in, in + size * 2, out);
}
void EQ::setBuffers_eq (EQ *a, float* in, float* out)
void EQ::setBuffers(float* _in, float* _out)
{
decalc_eq (a);
a->in = in;
a->out = out;
calc_eq (a);
decalc();
in = _in;
out = _out;
calc();
}
void EQ::setSamplerate_eq (EQ *a, int rate)
void EQ::setSamplerate(int _rate)
{
decalc_eq (a);
a->samplerate = rate;
calc_eq (a);
decalc();
samplerate = (float) _rate;
calc();
}
void EQ::setSize_eq (EQ *a, int size)
void EQ::setSize(int _size)
{
decalc_eq (a);
a->size = size;
calc_eq (a);
decalc();
size = _size;
calc();
}
/********************************************************************************************************
* *
* Overlap-Save Equalizer: RXA Properties *
* *
********************************************************************************************************/
/* // UNCOMMENT properties when a pointer is in place in rxa
PORT
void SetRXAEQRun (int channel, int run)
{
ch.csDSP.lock();
rxa.eq.p->run = run;
ch.csDSP.unlock();
}
PORT
void SetRXAEQProfile (int channel, int nfreqs, float* F, float* G)
{
EQ a;
ch.csDSP.lock();
a = rxa.eq.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = nfreqs;
a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
memcpy (a->F, F, (nfreqs + 1) * sizeof (float));
memcpy (a->G, G, (nfreqs + 1) * sizeof (float));
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetRXAEQCtfmode (int channel, int mode)
{
EQ a;
ch.csDSP.lock();
a = rxa.eq.p;
a->ctfmode = mode;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetRXAEQWintype (int channel, int wintype)
{
EQ a;
ch.csDSP.lock();
a = rxa.eq.p;
a->wintype = wintype;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetRXAGrphEQ (int channel, int *rxeq)
{ // three band equalizer (legacy compatibility)
EQ a;
ch.csDSP.lock();
a = rxa.eq.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 4;
a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (float)rxeq[0];
a->G[1] = (float)rxeq[1];
a->G[2] = (float)rxeq[1];
a->G[3] = (float)rxeq[2];
a->G[4] = (float)rxeq[3];
a->ctfmode = 0;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetRXAGrphEQ10 (int channel, int *rxeq)
{ // ten band equalizer (legacy compatibility)
EQ a;
int i;
ch.csDSP.lock();
a = rxa.eq.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 10;
a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (float)rxeq[i];
a->ctfmode = 0;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
*/
/********************************************************************************************************
* *
* Overlap-Save Equalizer: TXA Properties *
* *
********************************************************************************************************/
/* // UNCOMMENT properties when a pointer is in place in rxa
PORT
void SetTXAEQRun (int channel, int run)
{
ch.csDSP.lock();
txa.eq.p->run = run;
ch.csDSP.unlock();
}
PORT
void SetTXAEQProfile (int channel, int nfreqs, float* F, float* G)
{
EQ a;
ch.csDSP.lock();
a = txa.eq.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = nfreqs;
a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
memcpy (a->F, F, (nfreqs + 1) * sizeof (float));
memcpy (a->G, G, (nfreqs + 1) * sizeof (float));
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetTXAEQCtfmode (int channel, int mode)
{
EQ a;
ch.csDSP.lock();
a = txa.eq.p;
a->ctfmode = mode;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetTXAEQMethod (int channel, int wintype)
{
EQ a;
ch.csDSP.lock();
a = txa.eq.p;
a->wintype = wintype;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetTXAGrphEQ (int channel, int *txeq)
{ // three band equalizer (legacy compatibility)
EQ a;
ch.csDSP.lock();
a = txa.eq.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 4;
a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 150.0;
a->F[2] = 400.0;
a->F[3] = 1500.0;
a->F[4] = 6000.0;
a->G[0] = (float)txeq[0];
a->G[1] = (float)txeq[1];
a->G[2] = (float)txeq[1];
a->G[3] = (float)txeq[2];
a->G[4] = (float)txeq[3];
a->ctfmode = 0;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
PORT
void SetTXAGrphEQ10 (int channel, int *txeq)
{ // ten band equalizer (legacy compatibility)
EQ a;
int i;
ch.csDSP.lock();
a = txa.eq.p;
delete[] (a->G);
delete[] (a->F);
a->nfreqs = 10;
a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float));
a->F[1] = 32.0;
a->F[2] = 63.0;
a->F[3] = 125.0;
a->F[4] = 250.0;
a->F[5] = 500.0;
a->F[6] = 1000.0;
a->F[7] = 2000.0;
a->F[8] = 4000.0;
a->F[9] = 8000.0;
a->F[10] = 16000.0;
for (i = 0; i <= a->nfreqs; i++)
a->G[i] = (float)txeq[i];
a->ctfmode = 0;
delete[] (a->mults);
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
ch.csDSP.unlock();
}
*/
} // namespace WDSP

View File

@ -25,92 +25,6 @@ warren@wpratt.com
*/
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer *
* *
********************************************************************************************************/
#ifndef wdsp_eqp_h
#define wdsp_eqp_h
#include "export.h"
namespace WDSP {
class FIRCORE;
class RXA;
class TXA;
class WDSP_API EQP
{
public:
int run;
int size;
int nc;
int mp;
float* in;
float* out;
int nfreqs;
float* F;
float* G;
int ctfmode;
int wintype;
double samplerate;
FIRCORE *p;
static EQP* create_eqp (
int run,
int size,
int nc,
int mp,
float *in,
float *out,
int nfreqs,
float* F,
float* G,
int ctfmode,
int wintype,
int samplerate
);
static float* eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype);
static void destroy_eqp (EQP *a);
static void flush_eqp (EQP *a);
static void xeqp (EQP *a);
static void setBuffers_eqp (EQP *a, float* in, float* out);
static void setSamplerate_eqp (EQP *a, int rate);
static void setSize_eqp (EQP *a, int size);
// RXA
static void SetEQRun (RXA& rxa, int run);
static void SetEQNC (RXA& rxa, int nc);
static void SetEQMP (RXA& rxa, int mp);
static void SetEQProfile (RXA& rxa, int nfreqs, const float* F, const float* G);
static void SetEQCtfmode (RXA& rxa, int mode);
static void SetEQWintype (RXA& rxa, int wintype);
static void SetGrphEQ (RXA& rxa, int *rxeq);
static void SetGrphEQ10 (RXA& rxa, int *rxeq);
// TXA
static void SetEQRun (TXA& txa, int run);
static void SetEQNC (TXA& txa, int nc);
static void SetEQMP (TXA& txa, int mp);
static void SetEQProfile (TXA& txa, int nfreqs, const float* F, const float* G);
static void SetEQCtfmode (TXA& txa, int mode);
static void SetEQWintype (TXA& txa, int wintype);
static void SetGrphEQ (TXA& txa, int *txeq);
static void SetGrphEQ10 (TXA& txa, int *txeq);
static float* eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype);
private:
static int fEQcompare (const void * a, const void * b);
};
} // namespace WDSP
#endif
/********************************************************************************************************
* *
* Overlap-Save Equalizer *
@ -120,6 +34,8 @@ private:
#ifndef wdsp_eq_h
#define wdsp_eq_h
#include <vector>
#include "fftw3.h"
#include "export.h"
@ -133,11 +49,11 @@ public:
float* in;
float* out;
int nfreqs;
float* F;
float* G;
float* infilt;
float* product;
float* mults;
std::vector<float> F;
std::vector<float> G;
std::vector<float> infilt;
std::vector<float> product;
std::vector<float> mults;
float scale;
int ctfmode;
int wintype;
@ -145,18 +61,32 @@ public:
fftwf_plan CFor;
fftwf_plan CRev;
static EQ* create_eq (int run, int size, float *in, float *out, int nfreqs, float* F, float* G, int ctfmode, int wintype, int samplerate);
// static float* eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype);
static void destroy_eq (EQ *a);
static void flush_eq (EQ *a);
static void xeq (EQ *a);
static void setBuffers_eq (EQ *a, float* in, float* out);
static void setSamplerate_eq (EQ *a, int rate);
static void setSize_eq (EQ *a, int size);
EQ(
int run,
int size,
float *in,
float *out,
int nfreqs,
const float* F,
const float* G,
int ctfmode,
int wintype,
int samplerate
);
EQ(const EQ&) = delete;
EQ& operator=(EQ& other) = delete;
~EQ() = default;
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
private:
static void calc_eq (EQ *a);
static void decalc_eq (EQ *a);
static void eq_mults (std::vector<float>& mults, int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype);
void calc();
void decalc();
};
} // namespace WDSP

395
wdsp/eqp.cpp Normal file
View File

@ -0,0 +1,395 @@
/* eq.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013, 2016, 2017 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#include "comm.hpp"
#include "eqp.hpp"
#include "fircore.hpp"
#include "fir.hpp"
namespace WDSP {
int EQP::fEQcompare (const void * a, const void * b)
{
if (*(float*)a < *(float*)b)
return -1;
else if (*(float*)a == *(float*)b)
return 0;
else
return 1;
}
void EQP::eq_impulse (
std::vector<float>& impulse,
int N,
int _nfreqs,
const float* F,
const float* G,
double samplerate,
double scale,
int ctfmode,
int wintype
)
{
std::vector<float> fp(_nfreqs + 2);
std::vector<float> gp(_nfreqs + 2);
std::vector<float> A(N / 2 + 1);
float* sary = new float[2 * _nfreqs];
double gpreamp;
double f;
double frac;
int i;
int j;
int mid;
fp[0] = 0.0;
fp[_nfreqs + 1] = 1.0;
gpreamp = G[0];
for (i = 1; i <= _nfreqs; i++)
{
fp[i] = (float) (2.0 * F[i] / samplerate);
if (fp[i] < 0.0)
fp[i] = 0.0;
if (fp[i] > 1.0)
fp[i] = 1.0;
gp[i] = G[i];
}
for (i = 1, j = 0; i <= _nfreqs; i++, j+=2)
{
sary[j + 0] = fp[i];
sary[j + 1] = gp[i];
}
qsort (sary, _nfreqs, 2 * sizeof (float), fEQcompare);
for (i = 1, j = 0; i <= _nfreqs; i++, j+=2)
{
fp[i] = sary[j + 0];
gp[i] = sary[j + 1];
}
gp[0] = gp[1];
gp[_nfreqs + 1] = gp[_nfreqs];
mid = N / 2;
j = 0;
if (N & 1)
{
for (i = 0; i <= mid; i++)
{
f = (double)i / (double)mid;
while ((f > fp[j + 1]) && (j < _nfreqs))
j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
A[i] = (float) (pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale);
}
}
else
{
for (i = 0; i < mid; i++)
{
f = ((double)i + 0.5) / (double)mid;
while ((f > fp[j + 1]) && (j < _nfreqs))
j++;
frac = (f - fp[j]) / (fp[j + 1] - fp[j]);
A[i] = (float) (pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale);
}
}
if (ctfmode == 0)
{
int k;
int low;
int high;
double lowmag;
double highmag;
double flow4;
double fhigh4;
if (N & 1)
{
low = (int)(fp[1] * mid);
high = (int)(fp[_nfreqs] * mid + 0.5);
lowmag = A[low];
highmag = A[high];
flow4 = pow((double)low / (double)mid, 4.0);
fhigh4 = pow((double)high / (double)mid, 4.0);
k = low;
while (--k >= 0)
{
f = (double)k / (double)mid;
lowmag *= (f * f * f * f) / flow4;
if (lowmag < 1.0e-20) lowmag = 1.0e-20;
A[k] = (float) lowmag;
}
k = high;
while (++k <= mid)
{
f = (double)k / (double)mid;
highmag *= fhigh4 / (f * f * f * f);
if (highmag < 1.0e-20) highmag = 1.0e-20;
A[k] = (float) highmag;
}
}
else
{
low = (int)(fp[1] * mid - 0.5);
high = (int)(fp[_nfreqs] * mid - 0.5);
lowmag = A[low];
highmag = A[high];
flow4 = pow((double)low / (double)mid, 4.0);
fhigh4 = pow((double)high / (double)mid, 4.0);
k = low;
while (--k >= 0)
{
f = (double)k / (double)mid;
lowmag *= (f * f * f * f) / flow4;
if (lowmag < 1.0e-20) lowmag = 1.0e-20;
A[k] = (float) lowmag;
}
k = high;
while (++k < mid)
{
f = (double)k / (double)mid;
highmag *= fhigh4 / (f * f * f * f);
if (highmag < 1.0e-20) highmag = 1.0e-20;
A[k] = (float) highmag;
}
}
}
impulse.resize(2 * N);
if (N & 1)
FIR::fir_fsamp_odd(impulse, N, A.data(), 1, 1.0, wintype);
else
FIR::fir_fsamp(impulse, N, A.data(), 1, 1.0, wintype);
delete[] sary;
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer *
* *
********************************************************************************************************/
EQP::EQP(
int _run,
int _size,
int _nc,
int _mp,
float *_in,
float *_out,
int _nfreqs,
float* _F,
float* _G,
int _ctfmode,
int _wintype,
int _samplerate
)
{
// NOTE: 'nc' must be >= 'size'
std::vector<float> impulse;
run = _run;
size = _size;
nc = _nc;
mp = _mp;
in = _in;
out = _out;
nfreqs = _nfreqs;
F.resize(nfreqs + 1);
G.resize(nfreqs + 1);
std::copy(_F, _F + (_nfreqs + 1), F.begin());
std::copy(_G, _G + (_nfreqs + 1), G.begin());
ctfmode = _ctfmode;
wintype = _wintype;
samplerate = (double) _samplerate;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore = new FIRCORE(size, in, out, mp, impulse);
}
EQP::~EQP()
{
delete (fircore);
}
void EQP::flush()
{
fircore->flush();
}
void EQP::execute()
{
if (run)
fircore->execute();
else
std::copy(in, in + size * 2, out);
}
void EQP::setBuffers(float* _in, float* _out)
{
in = _in;
out = _out;
fircore->setBuffers(in, out);
}
void EQP::setSamplerate(int rate)
{
std::vector<float> impulse;
samplerate = rate;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
void EQP::setSize(int _size)
{
std::vector<float> impulse;
size = _size;
fircore->setSize(size);
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer: Public Properties *
* *
********************************************************************************************************/
void EQP::setRun(int _run)
{
run = _run;
}
void EQP::setNC(int _nc)
{
std::vector<float> impulse;
if (nc != _nc)
{
nc = _nc;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setNc(impulse);
}
}
void EQP::setMP(int _mp)
{
if (mp != _mp)
{
mp = _mp;
fircore->setMp(mp);
}
}
void EQP::setProfile(int _nfreqs, const float* _F, const float* _G)
{
std::vector<float> impulse;
nfreqs = _nfreqs;
F.resize(nfreqs + 1);
G.resize(nfreqs + 1);
std::copy(_F, _F + (_nfreqs + 1), F.begin());
std::copy(_G, _G + (_nfreqs + 1), G.begin());
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
void EQP::setCtfmode(int _mode)
{
std::vector<float> impulse;
ctfmode = _mode;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
void EQP::setWintype(int _wintype)
{
std::vector<float> impulse;
wintype = _wintype;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
void EQP::setGrphEQ(const int *rxeq)
{ // three band equalizer (legacy compatibility)
std::vector<float> impulse;
nfreqs = 4;
F.resize(nfreqs + 1);
G.resize(nfreqs + 1);
F[1] = 150.0;
F[2] = 400.0;
F[3] = 1500.0;
F[4] = 6000.0;
G[0] = (float)rxeq[0];
G[1] = (float)rxeq[1];
G[2] = (float)rxeq[1];
G[3] = (float)rxeq[2];
G[4] = (float)rxeq[3];
ctfmode = 0;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
void EQP::setGrphEQ10(const int *rxeq)
{ // ten band equalizer (legacy compatibility)
std::vector<float> impulse;
nfreqs = 10;
F.resize(nfreqs + 1);
G.resize(nfreqs + 1);
F[1] = 32.0;
F[2] = 63.0;
F[3] = 125.0;
F[4] = 250.0;
F[5] = 500.0;
F[6] = 1000.0;
F[7] = 2000.0;
F[8] = 4000.0;
F[9] = 8000.0;
F[10] = 16000.0;
for (int i = 0; i <= nfreqs; i++)
G[i] = (float)rxeq[i];
ctfmode = 0;
eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype);
fircore->setImpulse(impulse, 1);
}
} // namespace WDSP

113
wdsp/eqp.hpp Normal file
View File

@ -0,0 +1,113 @@
/* eq.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013, 2016 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
/********************************************************************************************************
* *
* Partitioned Overlap-Save Equalizer *
* *
********************************************************************************************************/
#ifndef wdsp_eqp_h
#define wdsp_eqp_h
#include <vector>
#include "export.h"
namespace WDSP {
class FIRCORE;
class WDSP_API EQP
{
public:
int run;
int size;
int nc;
int mp;
float* in;
float* out;
int nfreqs;
std::vector<float> F;
std::vector<float> G;
int ctfmode;
int wintype;
double samplerate;
FIRCORE *fircore;
EQP(
int run,
int size,
int nc,
int mp,
float *in,
float *out,
int nfreqs,
float* F,
float* G,
int ctfmode,
int wintype,
int samplerate
);
EQP(const EQP&) = delete;
EQP& operator=(const EQP& other) = delete;
~EQP();
void flush();
void execute();
void setBuffers(float* in, float* out);
void setSamplerate(int rate);
void setSize(int size);
// Public properties
void setRun(int run);
void setNC(int nc);
void setMP(int mp);
void setProfile(int nfreqs, const float* F, const float* G);
void setCtfmode(int mode);
void setWintype(int wintype);
void setGrphEQ(const int *rxeq);
void setGrphEQ10(const int *rxeq);
static void eq_impulse (
std::vector<float>& impulse,
int N,
int nfreqs,
const float* F,
const float* G,
double samplerate,
double scale,
int ctfmode,
int wintype
);
private:
static int fEQcompare (const void * a, const void * b);
};
} // namespace WDSP
#endif

View File

@ -31,12 +31,11 @@ warren@wpratt.com
namespace WDSP {
float* FCurve::fc_impulse (int nc, float f0, float f1, float g0, float, int curve, float samplerate, float scale, int ctfmode, int wintype)
void FCurve::fc_impulse (std::vector<float>& impulse, int nc, float f0, float f1, float g0, float, int curve, float samplerate, float scale, int ctfmode, int wintype)
{
float* A = new float[nc / 2 + 1]; // (float *) malloc0 ((nc / 2 + 1) * sizeof (float));
int i;
float fn, f;
float* impulse;
int mid = nc / 2;
float g0_lin = pow(10.0, g0 / 20.0);
if (nc & 1)
@ -140,22 +139,21 @@ float* FCurve::fc_impulse (int nc, float f0, float f1, float g0, float, int curv
}
}
}
if (nc & 1)
impulse = FIR::fir_fsamp_odd(nc, A, 1, 1.0, wintype);
FIR::fir_fsamp_odd(impulse, nc, A, 1, 1.0, wintype);
else
impulse = FIR::fir_fsamp(nc, A, 1, 1.0, wintype);
FIR::fir_fsamp(impulse, nc, A, 1, 1.0, wintype);
// print_impulse ("emph.txt", size + 1, impulse, 1, 0);
delete[] (A);
return impulse;
}
// generate mask for Overlap-Save Filter
float* FCurve::fc_mults (int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype)
void FCurve::fc_mults (std::vector<float>& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype)
{
float* impulse = fc_impulse (size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype);
float* mults = FIR::fftcv_mults(2 * size, impulse);
delete[] (impulse);
return mults;
std::vector<float> impulse(2 * (size + 1));
fc_impulse (impulse, size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype);
FIR::fftcv_mults(mults, 2 * size, impulse.data());
}
} // namespace WDSP

View File

@ -28,6 +28,8 @@ warren@wpratt.com
#ifndef wdsp_fcurve_h
#define wdsp_fcurve_h
#include <vector>
#include "export.h"
namespace WDSP {
@ -35,8 +37,8 @@ namespace WDSP {
class WDSP_API FCurve
{
public:
static float* fc_impulse (int nc, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype);
static float* fc_mults (int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype);
static void fc_impulse (std::vector<float>& impulse, int nc, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype);
static void fc_mults (std::vector<float>& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype);
};
} // namespace WDSP

View File

@ -27,6 +27,7 @@ warren@pratt.one
#define _CRT_SECURE_NO_WARNINGS
#include <limits>
#include <vector>
#include "fftw3.h"
#include "comm.hpp"
@ -34,134 +35,128 @@ warren@pratt.one
namespace WDSP {
float* FIR::fftcv_mults (int NM, float* c_impulse)
void FIR::fftcv_mults (std::vector<float>& mults, int NM, const float* c_impulse)
{
float* mults = new float[NM * 2];
float* cfft_impulse = new float[NM * 2];
mults.resize(NM * 2);
std::vector<float> cfft_impulse(NM * 2);
fftwf_plan ptmp = fftwf_plan_dft_1d(
NM,
(fftwf_complex *) cfft_impulse,
(fftwf_complex *) mults,
(fftwf_complex *) cfft_impulse.data(),
(fftwf_complex *) mults.data(),
FFTW_FORWARD,
FFTW_PATIENT
);
std::fill(cfft_impulse, cfft_impulse + NM * 2, 0);
std::fill(cfft_impulse.begin(), cfft_impulse.end(), 0);
// store complex coefs right-justified in the buffer
std::copy(c_impulse, c_impulse + (NM / 2 + 1) * 2, &(cfft_impulse[NM - 2]));
fftwf_execute (ptmp);
fftwf_destroy_plan (ptmp);
delete[] cfft_impulse;
return mults;
}
float* FIR::get_fsamp_window(int N, int wintype)
void FIR::get_fsamp_window(std::vector<float>& window, int N, int wintype)
{
int i;
double arg0, arg1;
float* window = new float[N]; // (float *) malloc0 (N * sizeof(float));
double arg0;
double arg1;
window.resize(N);
switch (wintype)
{
case 0:
arg0 = 2.0 * PI / ((double)N - 1.0);
for (i = 0; i < N; i++)
for (int i = 0; i < N; i++)
{
arg1 = cos(arg0 * (double)i);
window[i] = +0.21747
double val = +0.21747
+ arg1 * (-0.45325
+ arg1 * (+0.28256
+ arg1 * (-0.04672)));
window[i] = (float) val;
}
break;
case 1:
arg0 = 2.0 * PI / ((double)N - 1.0);
for (i = 0; i < N; ++i)
for (int i = 0; i < N; ++i)
{
arg1 = cos(arg0 * (double)i);
window[i] = +6.3964424114390378e-02
double val = +6.3964424114390378e-02
+ arg1 * (-2.3993864599352804e-01
+ arg1 * (+3.5015956323820469e-01
+ arg1 * (-2.4774111897080783e-01
+ arg1 * (+8.5438256055858031e-02
+ arg1 * (-1.2320203369293225e-02
+ arg1 * (+4.3778825791773474e-04))))));
window[i] = (float) val;
}
break;
default:
for (i = 0; i < N; i++)
for (int i = 0; i < N; i++)
window[i] = 1.0;
}
return window;
}
float* FIR::fir_fsamp_odd (int N, float* A, int rtype, double scale, int wintype)
void FIR::fir_fsamp_odd (std::vector<float>& c_impulse, int N, const float* A, int rtype, double scale, int wintype)
{
int i, j;
int mid = (N - 1) / 2;
double mag, phs;
float* window;
float *fcoef = new float[N * 2];
float *c_impulse = new float[N * 2];
double mag;
double phs;
std::vector<float> fcoef(N * 2);
fftwf_plan ptmp = fftwf_plan_dft_1d(
N,
(fftwf_complex *)fcoef,
(fftwf_complex *)c_impulse,
(fftwf_complex *)fcoef.data(),
(fftwf_complex *)c_impulse.data(),
FFTW_BACKWARD,
FFTW_PATIENT
);
double local_scale = 1.0 / (double) N;
for (i = 0; i <= mid; i++)
for (int i = 0; i <= mid; i++)
{
mag = A[i] * local_scale;
phs = - (double)mid * TWOPI * (double)i / (double)N;
fcoef[2 * i + 0] = mag * cos (phs);
fcoef[2 * i + 1] = mag * sin (phs);
fcoef[2 * i + 0] = (float) (mag * cos (phs));
fcoef[2 * i + 1] = (float) (mag * sin (phs));
}
for (i = mid + 1, j = 0; i < N; i++, j++)
for (int i = mid + 1, j = 0; i < N; i++, j++)
{
fcoef[2 * i + 0] = + fcoef[2 * (mid - j) + 0];
fcoef[2 * i + 1] = - fcoef[2 * (mid - j) + 1];
}
fftwf_execute (ptmp);
fftwf_destroy_plan (ptmp);
delete[] fcoef;
window = get_fsamp_window(N, wintype);
std::vector<float> window;
get_fsamp_window(window, N, wintype);
switch (rtype)
{
case 0:
for (i = 0; i < N; i++)
c_impulse[i] = scale * c_impulse[2 * i] * window[i];
for (int i = 0; i < N; i++)
c_impulse[i] = (float) (scale * c_impulse[2 * i] * window[i]);
break;
case 1:
for (i = 0; i < N; i++)
for (int i = 0; i < N; i++)
{
c_impulse[2 * i + 0] *= scale * window[i];
c_impulse[2 * i + 0] *= (float) (scale * window[i]);
c_impulse[2 * i + 1] = 0.0;
}
break;
default:
break;
}
delete[] window;
return c_impulse;
}
float* FIR::fir_fsamp (int N, float* A, int rtype, double scale, int wintype)
void FIR::fir_fsamp (std::vector<float>& c_impulse, int N, const float* A, int rtype, double scale, int wintype)
{
int n, i, j, k;
double sum;
float* window;
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
if (N & 1)
{
int M = (N - 1) / 2;
for (n = 0; n < M + 1; n++)
for (int n = 0; n < M + 1; n++)
{
sum = 0.0;
for (k = 1; k < M + 1; k++)
for (int k = 1; k < M + 1; k++)
sum += 2.0 * A[k] * cos(TWOPI * (n - M) * k / N);
c_impulse[2 * n + 0] = (1.0 / N) * (A[0] + sum);
c_impulse[2 * n + 0] = (float) ((1.0 / N) * (A[0] + sum));
c_impulse[2 * n + 1] = 0.0;
}
for (n = M + 1, j = 1; n < N; n++, j++)
for (int n = M + 1, j = 1; n < N; n++, j++)
{
c_impulse[2 * n + 0] = c_impulse[2 * (M - j) + 0];
c_impulse[2 * n + 1] = 0.0;
@ -170,80 +165,78 @@ float* FIR::fir_fsamp (int N, float* A, int rtype, double scale, int wintype)
else
{
double M = (double)(N - 1) / 2.0;
for (n = 0; n < N / 2; n++)
for (int n = 0; n < N / 2; n++)
{
sum = 0.0;
for (k = 1; k < N / 2; k++)
for (int k = 1; k < N / 2; k++)
sum += 2.0 * A[k] * cos(TWOPI * (n - M) * k / N);
c_impulse[2 * n + 0] = (1.0 / N) * (A[0] + sum);
c_impulse[2 * n + 0] = (float) ((1.0 / N) * (A[0] + sum));
c_impulse[2 * n + 1] = 0.0;
}
for (n = N / 2, j = 1; n < N; n++, j++)
for (int n = N / 2, j = 1; n < N; n++, j++)
{
c_impulse[2 * n + 0] = c_impulse[2 * (N / 2 - j) + 0];
c_impulse[2 * n + 1] = 0.0;
}
}
window = get_fsamp_window (N, wintype);
std::vector<float> window;
get_fsamp_window (window, N, wintype);
switch (rtype)
{
case 0:
for (i = 0; i < N; i++)
c_impulse[i] = scale * c_impulse[2 * i] * window[i];
for (int i = 0; i < N; i++)
c_impulse[i] = (float) (scale * c_impulse[2 * i] * window[i]);
break;
case 1:
for (i = 0; i < N; i++)
for (int i = 0; i < N; i++)
{
c_impulse[2 * i + 0] *= scale * window[i];
c_impulse[2 * i + 0] *= (float) (scale * window[i]);
c_impulse[2 * i + 1] = 0.0;
}
break;
default:
break;
}
delete[] window;
return c_impulse;
}
float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale)
void FIR::fir_bandpass (std::vector<float>& c_impulse, int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale)
{
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
c_impulse.resize(N * 2);
double ft = (f_high - f_low) / (2.0 * samplerate);
double ft_rad = TWOPI * ft;
double w_osc = PI * (f_high + f_low) / samplerate;
int i, j;
double m = 0.5 * (double)(N - 1);
double delta = PI / m;
double cosphi;
double posi, posj;
double sinc, window, coef;
double posi;
double posj;
double sinc;
double window;
double coef;
if (N & 1)
{
switch (rtype)
{
case 0:
c_impulse[N >> 1] = scale * 2.0 * ft;
c_impulse[N >> 1] = (float) (scale * 2.0 * ft);
break;
case 1:
c_impulse[N - 1] = scale * 2.0 * ft;
c_impulse[N - 1] = (float) (scale * 2.0 * ft);
c_impulse[ N ] = 0.0;
break;
default:
break;
}
}
for (i = (N + 1) / 2, j = N / 2 - 1; i < N; i++, j--)
for (int i = (N + 1) / 2, j = N / 2 - 1; i < N; i++, j--)
{
posi = (double)i - m;
posj = (double)j - m;
sinc = sin (ft_rad * posi) / (PI * posi);
switch (wintype)
if (wintype == 1) // Blackman-Harris 7-term
{
case 0: // Blackman-Harris 4-term
cosphi = cos (delta * i);
window = + 0.21747
+ cosphi * ( - 0.45325
+ cosphi * ( + 0.28256
+ cosphi * ( - 0.04672 )));
break;
case 1: // Blackman-Harris 7-term
cosphi = cos (delta * i);
window = + 6.3964424114390378e-02
+ cosphi * ( - 2.3993864599352804e-01
@ -252,27 +245,37 @@ float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate,
+ cosphi * ( + 8.5438256055858031e-02
+ cosphi * ( - 1.2320203369293225e-02
+ cosphi * ( + 4.3778825791773474e-04 ))))));
break;
}
else // Blackman-Harris 4-term
{
cosphi = cos (delta * i);
window = + 0.21747
+ cosphi * ( - 0.45325
+ cosphi * ( + 0.28256
+ cosphi * ( - 0.04672 )));
}
coef = scale * sinc * window;
switch (rtype)
{
case 0:
c_impulse[i] = + coef * cos (posi * w_osc);
c_impulse[j] = + coef * cos (posj * w_osc);
c_impulse[i] = (float) (+ coef * cos (posi * w_osc));
c_impulse[j] = (float) (+ coef * cos (posj * w_osc));
break;
case 1:
c_impulse[2 * i + 0] = + coef * cos (posi * w_osc);
c_impulse[2 * i + 1] = - coef * sin (posi * w_osc);
c_impulse[2 * j + 0] = + coef * cos (posj * w_osc);
c_impulse[2 * j + 1] = - coef * sin (posj * w_osc);
c_impulse[2 * i + 0] = (float) (+ coef * cos (posi * w_osc));
c_impulse[2 * i + 1] = (float) (- coef * sin (posi * w_osc));
c_impulse[2 * j + 0] = (float) (+ coef * cos (posj * w_osc));
c_impulse[2 * j + 1] = (float) (- coef * sin (posj * w_osc));
break;
default:
break;
}
}
return c_impulse;
}
float *FIR::fir_read (int N, const char *filename, int rtype, float scale)
void FIR::fir_read (std::vector<float>& c_impulse, int N, const char *filename, int rtype, float scale)
// N = number of real or complex coefficients (see rtype)
// *filename = filename
// rtype = 0: real coefficients
@ -282,11 +285,17 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale)
// NOTE: The number of values in the file must NOT exceed those implied by N and rtype
{
FILE *file;
int i;
float I, Q;
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
float I;
float Q;
c_impulse.resize(N * 2);
std::fill(c_impulse.begin(), c_impulse.end(), 0);
file = fopen (filename, "r");
for (i = 0; i < N; i++)
if (!file) {
return;
}
for (int i = 0; i < N; i++)
{
// read in the complex impulse response
// NOTE: IF the freq response is symmetrical about 0, the imag coeffs will all be zero.
@ -309,85 +318,83 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale)
c_impulse[2 * i + 1] = - scale * Q;
break;
}
default:
break;
}
}
fclose (file);
return c_impulse;
}
void FIR::analytic (int N, float* in, float* out)
{
if (N < 1) {
if (N < 2) {
return;
}
int i;
double inv_N = 1.0 / (double) N;
double two_inv_N = 2.0 * inv_N;
float* x = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
std::vector<float> x(N * 2);
fftwf_plan pfor = fftwf_plan_dft_1d (
N,
(fftwf_complex *) in,
(fftwf_complex *) x,
(fftwf_complex *) x.data(),
FFTW_FORWARD,
FFTW_PATIENT
);
fftwf_plan prev = fftwf_plan_dft_1d (
N,
(fftwf_complex *) x,
(fftwf_complex *) x.data(),
(fftwf_complex *) out,
FFTW_BACKWARD,
FFTW_PATIENT
);
fftwf_execute (pfor);
x[0] *= inv_N;
x[1] *= inv_N;
x[0] *= (float) inv_N;
x[1] *= (float) inv_N;
for (i = 1; i < N / 2; i++)
for (int i = 1; i < N / 2; i++)
{
x[2 * i + 0] *= two_inv_N;
x[2 * i + 1] *= two_inv_N;
x[2 * i + 0] *= (float) two_inv_N;
x[2 * i + 1] *= (float) two_inv_N;
}
x[N + 0] *= inv_N;
x[N + 1] *= inv_N;
x[N + 0] *= (float) inv_N;
x[N + 1] *= (float) inv_N;
memset (&x[N + 2], 0, (N - 2) * sizeof (float));
fftwf_execute (prev);
fftwf_destroy_plan (prev);
fftwf_destroy_plan (pfor);
delete[] x;
}
void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity)
void FIR::mp_imp (int N, std::vector<float>& fir, std::vector<float>& mpfir, int pfactor, int polarity)
{
int i;
int size = N * pfactor;
double inv_PN = 1.0 / (float)size;
float* firpad = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* firfreq = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
double* mag = new double[size]; // (float *) malloc0 (size * sizeof (float));
float* ana = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* impulse = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* newfreq = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
std::copy(fir, fir + N * 2, firpad);
double inv_PN = 1.0 / (double)size;
std::vector<float> firpad(size * 2);
std::vector<float> firfreq(size * 2);
std::vector<double> mag(size);
std::vector<float> ana(size * 2);
std::vector<float> impulse(size * 2);
std::vector<float> newfreq(size * 2);
std::copy(fir.begin(), fir.begin() + N * 2, firpad.begin());
fftwf_plan pfor = fftwf_plan_dft_1d (
size,
(fftwf_complex *) firpad,
(fftwf_complex *) firfreq,
(fftwf_complex *) firpad.data(),
(fftwf_complex *) firfreq.data(),
FFTW_FORWARD,
FFTW_PATIENT);
fftwf_plan prev = fftwf_plan_dft_1d (
size,
(fftwf_complex *) newfreq,
(fftwf_complex *) impulse,
(fftwf_complex *) newfreq.data(),
(fftwf_complex *) impulse.data(),
FFTW_BACKWARD,
FFTW_PATIENT
);
// print_impulse("orig_imp.txt", N, fir, 1, 0);
fftwf_execute (pfor);
for (i = 0; i < size; i++)
{
@ -395,52 +402,46 @@ void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity)
double xi = firfreq[2 * i + 1];
mag[i] = sqrt (xr*xr + xi*xi) * inv_PN;
if (mag[i] > 0.0)
ana[2 * i + 0] = log (mag[i]);
ana[2 * i + 0] = (float) log (mag[i]);
else
ana[2 * i + 0] = log (std::numeric_limits<float>::min());
}
analytic (size, ana, ana);
analytic (size, ana.data(), ana.data());
for (i = 0; i < size; i++)
{
newfreq[2 * i + 0] = + mag[i] * cos (ana[2 * i + 1]);
newfreq[2 * i + 0] = (float) (+ mag[i] * cos (ana[2 * i + 1]));
if (polarity)
newfreq[2 * i + 1] = + mag[i] * sin (ana[2 * i + 1]);
newfreq[2 * i + 1] = (float) (+ mag[i] * sin (ana[2 * i + 1]));
else
newfreq[2 * i + 1] = - mag[i] * sin (ana[2 * i + 1]);
newfreq[2 * i + 1] = (float) (- mag[i] * sin (ana[2 * i + 1]));
}
fftwf_execute (prev);
if (polarity)
std::copy(&impulse[2 * (pfactor - 1) * N], &impulse[2 * (pfactor - 1) * N] + N * 2, mpfir);
std::copy(&impulse[2 * (pfactor - 1) * N], &impulse[2 * (pfactor - 1) * N] + N * 2, mpfir.begin());
else
std::copy(impulse, impulse + N * 2, mpfir);
// print_impulse("min_imp.txt", N, mpfir, 1, 0);
std::copy(impulse.begin(), impulse.end(), mpfir.begin());
fftwf_destroy_plan (prev);
fftwf_destroy_plan (pfor);
delete[] (newfreq);
delete[] (impulse);
delete[] (ana);
delete[] (mag);
delete[] (firfreq);
delete[] (firpad);
}
// impulse response of a zero frequency filter comprising a cascade of two resonators,
// each followed by a detrending filter
float* FIR::zff_impulse(int nc, float scale)
void FIR::zff_impulse(std::vector<float>& c_dresdet, int nc, float scale)
{
// nc = number of coefficients (power of two)
int n_resdet = nc / 2 - 1; // size of single zero-frequency resonator with detrender
int n_dresdet = 2 * n_resdet - 1; // size of two cascaded units; when we convolve these we get 2 * n - 1 length
// allocate the single and make the values
float* resdet = new float[n_resdet]; // (float*)malloc0 (n_resdet * sizeof(float));
std::vector<float> resdet(n_resdet); // (float*)malloc0 (n_resdet * sizeof(float));
for (int i = 1, j = 0, k = n_resdet - 1; i < nc / 4; i++, j++, k--)
resdet[j] = resdet[k] = (float)(i * (i + 1) / 2);
resdet[nc / 4 - 1] = (float)(nc / 4 * (nc / 4 + 1) / 2);
// print_impulse ("resdet", n_resdet, resdet, 0, 0);
// allocate the float and complex versions and make the values
float* dresdet = new float[n_dresdet]; // (float*)malloc0 (n_dresdet * sizeof(float));
float div = (float)((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor
float* c_dresdet = new float[nc * 2]; // (float*)malloc0 (nc * sizeof(complex));
std::vector<float> dresdet(n_dresdet);
auto div = (float) ((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor
c_dresdet.resize(nc * 2);
for (int n = 0; n < n_dresdet; n++) // convolve to make the cascade
{
for (int k = 0; k < n_resdet; k++)
@ -450,11 +451,6 @@ float* FIR::zff_impulse(int nc, float scale)
c_dresdet[2 * n + 0] = dresdet[n] * scale;
c_dresdet[2 * n + 1] = 0.0;
}
// print_impulse("dresdet", n_dresdet, dresdet, 0, 0);
// print_impulse("c_dresdet", nc, c_dresdet, 1, 0);
delete[] (dresdet);
delete[] (resdet);
return c_dresdet;
}
} // namespace WDSP

View File

@ -27,6 +27,8 @@ warren@pratt.one
#ifndef wdsp_fir_h
#define wdsp_fir_h
#include <vector>
#include "export.h"
namespace WDSP {
@ -34,17 +36,17 @@ namespace WDSP {
class WDSP_API FIR
{
public:
static float* fftcv_mults (int NM, float* c_impulse);
static float* fir_fsamp_odd (int N, float* A, int rtype, double scale, int wintype);
static float* fir_fsamp (int N, float* A, int rtype, double scale, int wintype);
static float* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale);
static void mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity);
static void fftcv_mults (std::vector<float>& mults, int NM, const float* impulse);
static void fir_fsamp_odd (std::vector<float>& c_impulse, int N, const float* A, int rtype, double scale, int wintype);
static void fir_fsamp (std::vector<float>& c_impulse, int N, const float* A, int rtype, double scale, int wintype);
static void fir_bandpass (std::vector<float>& impulse, int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale);
static void mp_imp (int N, std::vector<float>& fir, std::vector<float>& mpfir, int pfactor, int polarity);
private:
static void analytic (int N, float* in, float* out);
static float* get_fsamp_window(int N, int wintype);
static float *fir_read (int N, const char *filename, int rtype, float scale);
static float* zff_impulse(int nc, float scale);
static void get_fsamp_window(std::vector<float>& window, int N, int wintype);
static void fir_read (std::vector<float>& impulse, int N, const char *filename, int rtype, float scale);
static void zff_impulse(std::vector<float>& impulse, int nc, float scale);
};
#endif

View File

@ -38,223 +38,209 @@ namespace WDSP {
********************************************************************************************************/
void FIRCORE::plan_fircore (FIRCORE *a)
void FIRCORE::plan()
{
// must call for change in 'nc', 'size', 'out'
int i;
a->nfor = a->nc / a->size;
a->cset = 0;
a->buffidx = 0;
a->idxmask = a->nfor - 1;
a->fftin = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex));
a->fftout = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *));
a->fmask = new float**[2]; // (float ***) malloc0 (2 * sizeof (float **));
a->fmask[0] = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *));
a->fmask[1] = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *));
a->maskgen = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex));
a->pcfor = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan));
a->maskplan = new fftwf_plan*[2]; // (fftwf_plan **) malloc0 (2 * sizeof (fftwf_plan *));
a->maskplan[0] = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan));
a->maskplan[1] = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan));
for (i = 0; i < a->nfor; i++)
nfor = nc / size;
cset = 0;
buffidx = 0;
idxmask = nfor - 1;
fftin.resize(2 * size * 2);
fftout.resize(nfor);
fmask[0].resize(nfor);
fmask[1].resize(nfor);
maskgen.resize(2 * size * 2);
pcfor.resize(nfor);
maskplan[0].resize(nfor);
maskplan[1].resize(nfor);
for (int i = 0; i < nfor; i++)
{
a->fftout[i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex));
a->fmask[0][i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex));
a->fmask[1][i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex));
a->pcfor[i] = fftwf_plan_dft_1d(
2 * a->size,
(fftwf_complex *)a->fftin,
(fftwf_complex *)a->fftout[i],
fftout[i].resize(2 * size * 2);
fmask[0][i].resize(2 * size * 2);
fmask[1][i].resize(2 * size * 2);
pcfor[i] = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *)fftin.data(),
(fftwf_complex *)fftout[i].data(),
FFTW_FORWARD,
FFTW_PATIENT
);
a->maskplan[0][i] = fftwf_plan_dft_1d(
2 * a->size,
(fftwf_complex *)a->maskgen,
(fftwf_complex *)a->fmask[0][i],
maskplan[0][i] = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *)maskgen.data(),
(fftwf_complex *)fmask[0][i].data(),
FFTW_FORWARD,
FFTW_PATIENT
);
a->maskplan[1][i] = fftwf_plan_dft_1d(
2 * a->size,
(fftwf_complex *)a->maskgen,
(fftwf_complex *)a->fmask[1][i],
maskplan[1][i] = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *)maskgen.data(),
(fftwf_complex *)fmask[1][i].data(),
FFTW_FORWARD,
FFTW_PATIENT
);
}
a->accum = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex));
a->crev = fftwf_plan_dft_1d(
2 * a->size,
(fftwf_complex *)a->accum,
(fftwf_complex *)a->out,
accum.resize(2 * size * 2);
crev = fftwf_plan_dft_1d(
2 * size,
(fftwf_complex *)accum.data(),
(fftwf_complex *)out,
FFTW_BACKWARD,
FFTW_PATIENT
);
a->masks_ready = 0;
masks_ready = 0;
}
void FIRCORE::calc_fircore (FIRCORE *a, int flip)
void FIRCORE::calc(int _flip)
{
// call for change in frequency, rate, wintype, gain
// must also call after a call to plan_firopt()
int i;
if (a->mp)
FIR::mp_imp (a->nc, a->impulse, a->imp, 16, 0);
if (mp)
FIR::mp_imp (nc, impulse, imp, 16, 0);
else
std::copy(a->impulse, a->impulse + a->nc * 2, a->imp);
std::copy(impulse.begin(), impulse.end(), imp.begin());
for (i = 0; i < a->nfor; i++)
for (int i = 0; i < nfor; i++)
{
// I right-justified the impulse response => take output from left side of output buff, discard right side
// Be careful about flipping an asymmetrical impulse response.
std::copy(&(a->imp[2 * a->size * i]), &(a->imp[2 * a->size * i]) + a->size * 2, &(a->maskgen[2 * a->size]));
fftwf_execute (a->maskplan[1 - a->cset][i]);
std::copy(&(imp[2 * size * i]), &(imp[2 * size * i]) + size * 2, &(maskgen[2 * size]));
fftwf_execute (maskplan[1 - cset][i]);
}
a->masks_ready = 1;
masks_ready = 1;
if (flip)
if (_flip)
{
a->cset = 1 - a->cset;
a->masks_ready = 0;
cset = 1 - cset;
masks_ready = 0;
}
}
FIRCORE* FIRCORE::create_fircore (int size, float* in, float* out, int nc, int mp, float* impulse)
FIRCORE::FIRCORE(
int _size,
float* _in,
float* _out,
int _mp,
const std::vector<float>& _impulse
)
{
FIRCORE *a = new FIRCORE;
a->size = size;
a->in = in;
a->out = out;
a->nc = nc;
a->mp = mp;
// InitializeCriticalSectionAndSpinCount (&a->update, 2500);
plan_fircore (a);
a->impulse = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex));
a->imp = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex));
std::copy(impulse, impulse + a->nc * 2, a->impulse);
calc_fircore (a, 1);
return a;
size = _size;
in = _in;
out = _out;
nc = (int) (_impulse.size() / 2);
mp = _mp;
plan();
impulse.resize(_impulse.size());
imp.resize(_impulse.size());
std::copy(_impulse.begin(), _impulse.end(), impulse.begin());
calc(1);
}
void FIRCORE::deplan_fircore (FIRCORE *a)
void FIRCORE::deplan()
{
int i;
fftwf_destroy_plan (a->crev);
delete[] (a->accum);
for (i = 0; i < a->nfor; i++)
fftwf_destroy_plan (crev);
for (int i = 0; i < nfor; i++)
{
delete[] (a->fftout[i]);
delete[] (a->fmask[0][i]);
delete[] (a->fmask[1][i]);
fftwf_destroy_plan (a->pcfor[i]);
fftwf_destroy_plan (a->maskplan[0][i]);
fftwf_destroy_plan (a->maskplan[1][i]);
fftwf_destroy_plan (pcfor[i]);
fftwf_destroy_plan (maskplan[0][i]);
fftwf_destroy_plan (maskplan[1][i]);
}
delete[] (a->maskplan[0]);
delete[] (a->maskplan[1]);
delete[] (a->maskplan);
delete[] (a->pcfor);
delete[] (a->maskgen);
delete[] (a->fmask[0]);
delete[] (a->fmask[1]);
delete[] (a->fmask);
delete[] (a->fftout);
delete[] (a->fftin);
}
void FIRCORE::destroy_fircore (FIRCORE *a)
FIRCORE::~FIRCORE()
{
deplan_fircore (a);
delete[] (a->imp);
delete[] (a->impulse);
delete (a);
deplan();
}
void FIRCORE::flush_fircore (FIRCORE *a)
void FIRCORE::flush()
{
int i;
std::fill(a->fftin, a->fftin + 2 * a->size * 2, 0);
for (i = 0; i < a->nfor; i++)
std::fill(a->fftout[i], a->fftout[i] + 2 * a->size * 2, 0);
a->buffidx = 0;
std::fill(fftin.begin(), fftin.end(), 0);
for (int i = 0; i < nfor; i++)
std::fill(fftout[i].begin(), fftout[i].end(), 0);
buffidx = 0;
}
void FIRCORE::xfircore (FIRCORE *a)
void FIRCORE::execute()
{
int i, j, k;
std::copy(a->in, a->in + a->size * 2, &(a->fftin[2 * a->size]));
fftwf_execute (a->pcfor[a->buffidx]);
k = a->buffidx;
std::fill(a->accum, a->accum + 2 * a->size * 2, 0);
int k;
std::copy(in, in + size * 2, &(fftin[2 * size]));
fftwf_execute (pcfor[buffidx]);
k = buffidx;
std::fill(accum.begin(), accum.end(), 0);
for (j = 0; j < a->nfor; j++)
for (int j = 0; j < nfor; j++)
{
for (i = 0; i < 2 * a->size; i++)
for (int i = 0; i < 2 * size; i++)
{
a->accum[2 * i + 0] += a->fftout[k][2 * i + 0] * a->fmask[a->cset][j][2 * i + 0] - a->fftout[k][2 * i + 1] * a->fmask[a->cset][j][2 * i + 1];
a->accum[2 * i + 1] += a->fftout[k][2 * i + 0] * a->fmask[a->cset][j][2 * i + 1] + a->fftout[k][2 * i + 1] * a->fmask[a->cset][j][2 * i + 0];
accum[2 * i + 0] += fftout[k][2 * i + 0] * fmask[cset][j][2 * i + 0] - fftout[k][2 * i + 1] * fmask[cset][j][2 * i + 1];
accum[2 * i + 1] += fftout[k][2 * i + 0] * fmask[cset][j][2 * i + 1] + fftout[k][2 * i + 1] * fmask[cset][j][2 * i + 0];
}
k = (k + a->idxmask) & a->idxmask;
k = (k + idxmask) & idxmask;
}
a->buffidx = (a->buffidx + 1) & a->idxmask;
fftwf_execute (a->crev);
std::copy(&(a->fftin[2 * a->size]), &(a->fftin[2 * a->size]) + a->size * 2, a->fftin);
buffidx = (buffidx + 1) & idxmask;
fftwf_execute (crev);
std::copy(&(fftin[2 * size]), &(fftin[2 * size]) + size * 2, fftin.begin());
}
void FIRCORE::setBuffers_fircore (FIRCORE *a, float* in, float* out)
void FIRCORE::setBuffers(float* _in, float* _out)
{
a->in = in;
a->out = out;
deplan_fircore (a);
plan_fircore (a);
calc_fircore (a, 1);
in = _in;
out = _out;
deplan();
plan();
calc(1);
}
void FIRCORE::setSize_fircore (FIRCORE *a, int size)
void FIRCORE::setSize(int _size)
{
a->size = size;
deplan_fircore (a);
plan_fircore (a);
calc_fircore (a, 1);
size = _size;
deplan();
plan();
calc(1);
}
void FIRCORE::setImpulse_fircore (FIRCORE *a, float* impulse, int update)
void FIRCORE::setImpulse(const std::vector<float>& _impulse, int _update)
{
std::copy(impulse, impulse + a->nc * 2, a->impulse);
calc_fircore (a, update);
auto imp_nc = (int) (_impulse.size() / 2);
if (imp_nc == nc) // to be on the safe side but setNc would be called if impulse size changes
{
std::copy(_impulse.begin(), _impulse.end(), impulse.begin());
calc(_update);
}
else{
setNc(_impulse);
}
}
void FIRCORE::setNc_fircore (FIRCORE *a, int nc, float* impulse)
void FIRCORE::setNc(const std::vector<float>& _impulse)
{
// because of FFT planning, this will probably cause a glitch in audio if done during dataflow
deplan_fircore (a);
delete[] (a->impulse);
delete[] (a->imp);
a->nc = nc;
plan_fircore (a);
a->imp = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex));
a->impulse = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex));
std::copy(impulse, impulse + a->nc * 2, a->impulse);
calc_fircore (a, 1);
deplan();
nc = (int) (_impulse.size() / 2);
plan();
imp.resize(nc * 2);
impulse.resize(nc * 2);
std::copy(_impulse.begin(), _impulse.end(), impulse.begin());
calc(1);
}
void FIRCORE::setMp_fircore (FIRCORE *a, int mp)
void FIRCORE::setMp(int _mp)
{
a->mp = mp;
calc_fircore (a, 1);
mp = _mp;
calc(1);
}
void FIRCORE::setUpdate_fircore (FIRCORE *a)
void FIRCORE::setUpdate()
{
if (a->masks_ready)
if (masks_ready)
{
a->cset = 1 - a->cset;
a->masks_ready = 0;
cset = 1 - cset;
masks_ready = 0;
}
}

Some files were not shown because too many files have changed in this diff Show More