mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-02-26 11:20:06 -05:00
Refactored WDSP initial commit
This commit is contained in:
parent
98baa03619
commit
cd4ad2cc95
8
.gitignore
vendored
8
.gitignore
vendored
@ -43,4 +43,10 @@ obj-x86_64-linux-gnu/*
|
||||
|
||||
/rescuesdriq/vendor/
|
||||
/rescuesdriq/Godeps/
|
||||
/.vs
|
||||
/.vs
|
||||
|
||||
# WDSP
|
||||
wdsp/*.o
|
||||
wdsp/*.h
|
||||
wdsp/*.c
|
||||
wdsp/Makefile
|
||||
|
||||
@ -857,6 +857,7 @@ if (FFTW3F_FOUND)
|
||||
add_subdirectory(ft8)
|
||||
add_definitions(-DHAS_FT8)
|
||||
set(FT8_SUPPORT ON CACHE INTERNAL "")
|
||||
add_subdirectory(wdsp)
|
||||
endif()
|
||||
|
||||
add_subdirectory(sdrbench)
|
||||
|
||||
@ -167,4 +167,16 @@
|
||||
# define FT8_API
|
||||
#endif
|
||||
|
||||
/* the 'WDSP_API' controls the import/export of 'wdsp' symbols
|
||||
*/
|
||||
#if !defined(sdrangel_STATIC)
|
||||
# ifdef wdsp_EXPORTS
|
||||
# define WDSP_API __SDR_EXPORT
|
||||
# else
|
||||
# define WDSP_API __SDR_IMPORT
|
||||
# endif
|
||||
#else
|
||||
# define WDSP_API
|
||||
#endif
|
||||
|
||||
#endif /* __SDRANGEL_EXPORT_H */
|
||||
|
||||
135
wdsp/CMakeLists.txt
Normal file
135
wdsp/CMakeLists.txt
Normal file
@ -0,0 +1,135 @@
|
||||
project(wdsp)
|
||||
|
||||
set(wdsp_SOURCES
|
||||
../custom/apple/apple_compat.c
|
||||
amd.cpp
|
||||
ammod.cpp
|
||||
amsq.cpp
|
||||
anf.cpp
|
||||
anr.cpp
|
||||
bandpass.cpp
|
||||
bldr.cpp
|
||||
bps.cpp
|
||||
calculus.cpp
|
||||
cblock.cpp
|
||||
cfcomp.cpp
|
||||
cfir.cpp
|
||||
compress.cpp
|
||||
delay.cpp
|
||||
emnr.cpp
|
||||
emph.cpp
|
||||
eq.cpp
|
||||
fcurve.cpp
|
||||
fir.cpp
|
||||
firmin.cpp
|
||||
fmd.cpp
|
||||
fmmod.cpp
|
||||
fmsq.cpp
|
||||
gain.cpp
|
||||
gen.cpp
|
||||
icfir.cpp
|
||||
iir.cpp
|
||||
iqc.cpp
|
||||
lmath.cpp
|
||||
meter.cpp
|
||||
meterlog10.cpp
|
||||
nbp.cpp
|
||||
osctrl.cpp
|
||||
patchpanel.cpp
|
||||
resample.cpp
|
||||
rmatch.cpp
|
||||
RXA.cpp
|
||||
sender.cpp
|
||||
shift.cpp
|
||||
siphon.cpp
|
||||
slew.cpp
|
||||
snb.cpp
|
||||
ssql.cpp
|
||||
TXA.cpp
|
||||
varsamp.cpp
|
||||
wcpAGC.cpp
|
||||
)
|
||||
|
||||
set(wdsp_HEADERS
|
||||
amd.hpp
|
||||
ammod.hpp
|
||||
amsq.hpp
|
||||
anf.hpp
|
||||
anr.hpp
|
||||
bandpass.hpp
|
||||
bldr.hpp
|
||||
bps.hpp
|
||||
calculus.hpp
|
||||
cblock.hpp
|
||||
cfcomp.hpp
|
||||
cfir.hpp
|
||||
comm.hpp
|
||||
compress.hpp
|
||||
delay.hpp
|
||||
emnr.hpp
|
||||
emph.hpp
|
||||
eq.hpp
|
||||
fcurve.hpp
|
||||
fir.hpp
|
||||
firmin.hpp
|
||||
fmd.hpp
|
||||
fmmod.hpp
|
||||
fmsq.hpp
|
||||
gain.hpp
|
||||
gen.hpp
|
||||
icfir.hpp
|
||||
iir.hpp
|
||||
iqc.hpp
|
||||
lmath.hpp
|
||||
meter.hpp
|
||||
meterlog10.hpp
|
||||
nbp.hpp
|
||||
osctrl.hpp
|
||||
patchpanel.hpp
|
||||
resample.hpp
|
||||
rmatch.hpp
|
||||
RXA.hpp
|
||||
sender.hpp
|
||||
shift.hpp
|
||||
siphon.hpp
|
||||
slew.hpp
|
||||
snb.hpp
|
||||
ssql.hpp
|
||||
TXA.hpp
|
||||
varsamp.hpp
|
||||
wcpAGC.hpp
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/exports
|
||||
${CUSTOM_APPLE_INCLUDE}
|
||||
${FFTW3F_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_library(wdsp SHARED
|
||||
${wdsp_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(wdsp
|
||||
${FFTW3F_LIBRARIES}
|
||||
Qt::Core
|
||||
)
|
||||
|
||||
set_target_properties(wdsp PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS true)
|
||||
if (MSVC)
|
||||
set_target_properties(wdsp PROPERTIES INTERPROCEDURAL_OPTIMIZATION false)
|
||||
endif()
|
||||
|
||||
install(TARGETS wdsp DESTINATION ${INSTALL_LIB_DIR})
|
||||
|
||||
if (LINUX)
|
||||
add_executable(wdsp_make_interface
|
||||
make_interface.cpp
|
||||
)
|
||||
|
||||
add_executable(wdsp_make_calculus
|
||||
make_calculus.cpp
|
||||
)
|
||||
install(TARGETS wdsp_make_interface wdsp_make_calculus DESTINATION ${INSTALL_BIN_DIR})
|
||||
endif()
|
||||
|
||||
340
wdsp/COPYING
Normal file
340
wdsp/COPYING
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
5
wdsp/README.md
Normal file
5
wdsp/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# wdsp
|
||||
WDSP by Warren Pratt, NR0V
|
||||
DSP Library originally written for Windows
|
||||
Ported to Linux and Android by John Melton g0orx/n6lyt
|
||||
Adapted to SDRangel by Edouard Griffiths, F4EXB
|
||||
1045
wdsp/RXA.cpp
Normal file
1045
wdsp/RXA.cpp
Normal file
File diff suppressed because it is too large
Load Diff
243
wdsp/RXA.hpp
Normal file
243
wdsp/RXA.hpp
Normal file
@ -0,0 +1,243 @@
|
||||
/* RXA.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2014, 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_rxa_h
|
||||
#define wdsp_rxa_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "unit.hpp"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class METER;
|
||||
class SHIFT;
|
||||
class RESAMPLE;
|
||||
class GEN;
|
||||
class BANDPASS;
|
||||
class BPS;
|
||||
class SNB;
|
||||
class NOTCHDB;
|
||||
class NBP;
|
||||
class BPSNBA;
|
||||
class SNBA;
|
||||
class SENDER;
|
||||
class AMSQ;
|
||||
class AMD;
|
||||
class FMD;
|
||||
class FMSQ;
|
||||
class EQP;
|
||||
class ANF;
|
||||
class ANR;
|
||||
class EMNR;
|
||||
class WCPAGC;
|
||||
class SPEAK;
|
||||
class MPEAK;
|
||||
class PANEL;
|
||||
class SIPHON;
|
||||
class CBL;
|
||||
class SSQL;
|
||||
|
||||
class WDSP_API RXA : public Unit
|
||||
{
|
||||
public:
|
||||
enum rxaMode
|
||||
{
|
||||
RXA_LSB,
|
||||
RXA_USB,
|
||||
RXA_DSB,
|
||||
RXA_CWL,
|
||||
RXA_CWU,
|
||||
RXA_FM,
|
||||
RXA_AM,
|
||||
RXA_DIGU,
|
||||
RXA_SPEC,
|
||||
RXA_DIGL,
|
||||
RXA_SAM,
|
||||
RXA_DRM
|
||||
};
|
||||
|
||||
enum rxaMeterType
|
||||
{
|
||||
RXA_S_PK,
|
||||
RXA_S_AV,
|
||||
RXA_ADC_PK,
|
||||
RXA_ADC_AV,
|
||||
RXA_AGC_GAIN,
|
||||
RXA_AGC_PK,
|
||||
RXA_AGC_AV,
|
||||
RXA_METERTYPE_LAST
|
||||
};
|
||||
|
||||
double* inbuff;
|
||||
double* outbuff;
|
||||
double* midbuff;
|
||||
int mode;
|
||||
double meter[RXA_METERTYPE_LAST];
|
||||
QRecursiveMutex* pmtupdate[RXA_METERTYPE_LAST];
|
||||
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;
|
||||
|
||||
static RXA* create_rxa (
|
||||
int in_rate, // input samplerate
|
||||
int out_rate, // output samplerate
|
||||
int in_size, // input buffsize (complex samples) in a fexchange() operation
|
||||
int dsp_rate, // sample rate for mainstream dsp processing
|
||||
int dsp_size, // number complex samples processed per buffer in mainstream dsp processing
|
||||
int dsp_insize, // size (complex samples) of the output of the r1 (input) buffer
|
||||
int dsp_outsize, // size (complex samples) of the input of the r2 (output) buffer
|
||||
int out_size // output buffsize (complex samples) in a fexchange() operation
|
||||
);
|
||||
static void destroy_rxa (RXA *rxa);
|
||||
static void flush_rxa (RXA *rxa);
|
||||
static void xrxa (RXA *rxa);
|
||||
static void setInputSamplerate (RXA *rxa, int dsp_insize, int in_rate);
|
||||
static void setOutputSamplerate (RXA *rxa, int dsp_outsize, int out_rate);
|
||||
static void setDSPSamplerate (RXA *rxa, int dsp_insize, int dsp_outsize, int dsp_rate);
|
||||
static void setDSPBuffsize (RXA *rxa, int dsp_insize, int dsp_size, int dsp_outsize);
|
||||
|
||||
// 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);
|
||||
|
||||
// Collectives
|
||||
static void SetPassband (RXA& rxa, double f_low, double f_high);
|
||||
static void SetNC (RXA& rxa, int nc);
|
||||
static void SetMP (RXA& rxa, int mp);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
998
wdsp/TXA.cpp
Normal file
998
wdsp/TXA.cpp
Normal file
@ -0,0 +1,998 @@
|
||||
/* TXA.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2014, 2016, 2017, 2021, 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 "ammod.hpp"
|
||||
#include "meter.hpp"
|
||||
#include "resample.hpp"
|
||||
#include "patchpanel.hpp"
|
||||
#include "amsq.hpp"
|
||||
#include "eq.hpp"
|
||||
#include "iir.hpp"
|
||||
#include "cfcomp.hpp"
|
||||
#include "compress.hpp"
|
||||
#include "bandpass.hpp"
|
||||
#include "bps.hpp"
|
||||
#include "osctrl.hpp"
|
||||
#include "wcpAGC.hpp"
|
||||
#include "emph.hpp"
|
||||
#include "fmmod.hpp"
|
||||
#include "siphon.hpp"
|
||||
#include "gen.hpp"
|
||||
#include "slew.hpp"
|
||||
#include "iqc.hpp"
|
||||
#include "cfir.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
TXA* TXA::create_txa (
|
||||
int in_rate, // input samplerate
|
||||
int out_rate, // output samplerate
|
||||
int in_size, // input buffsize (complex samples) in a fexchange() operation
|
||||
int dsp_rate, // sample rate for mainstream dsp processing
|
||||
int dsp_size, // number complex samples processed per buffer in mainstream dsp processing
|
||||
int dsp_insize, // size (complex samples) of the output of the r1 (input) buffer
|
||||
int dsp_outsize, // size (complex samples) of the input of the r2 (output) buffer
|
||||
int out_size // output buffsize (complex samples) in a fexchange() operation
|
||||
)
|
||||
{
|
||||
TXA *txa = new TXA;
|
||||
|
||||
txa->in_rate = in_rate;
|
||||
txa->out_rate = out_rate;
|
||||
txa->in_size = in_size;
|
||||
txa->dsp_rate = dsp_rate;
|
||||
txa->dsp_size = dsp_size;
|
||||
txa->dsp_insize = dsp_insize;
|
||||
txa->dsp_outsize = dsp_outsize;
|
||||
txa->out_size = out_size;
|
||||
|
||||
txa->mode = TXA_LSB;
|
||||
txa->f_low = -5000.0;
|
||||
txa->f_high = - 100.0;
|
||||
txa->inbuff = new double[1 * txa->dsp_insize * 2]; // (double *) malloc0 (1 * txa->dsp_insize * sizeof (complex));
|
||||
txa->outbuff = new double[1 * txa->dsp_outsize * 2]; // (double *) malloc0 (1 * txa->dsp_outsize * sizeof (complex));
|
||||
txa->midbuff = new double[3 * txa->dsp_size * 2]; //(double *) malloc0 (2 * txa->dsp_size * sizeof (complex));
|
||||
|
||||
txa->rsmpin.p = RESAMPLE::create_resample (
|
||||
0, // run - will be turned on below if needed
|
||||
txa->dsp_insize, // input buffer size
|
||||
txa->inbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
txa->in_rate, // input sample rate
|
||||
txa->dsp_rate, // output sample rate
|
||||
0.0, // select cutoff automatically
|
||||
0, // select ncoef automatically
|
||||
1.0); // gain
|
||||
|
||||
txa->gen0.p = GEN::create_gen (
|
||||
0, // run
|
||||
txa->dsp_size, // buffer size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->dsp_rate, // sample rate
|
||||
2); // mode
|
||||
|
||||
txa->panel.p = PANEL::create_panel (
|
||||
1, // run
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
1.0, // gain1
|
||||
1.0, // gain2I
|
||||
1.0, // gain2Q
|
||||
2, // 1 to use Q, 2 to use I for input
|
||||
0); // 0, no copy
|
||||
|
||||
txa->phrot.p = PHROT::create_phrot (
|
||||
0, // run
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
338.0, // 1/2 of phase frequency
|
||||
8); // number of stages
|
||||
|
||||
txa->micmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
0, // optional pointer to another 'run'
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_MIC_AV, // index for average value
|
||||
TXA_MIC_PK, // index for peak value
|
||||
-1, // index for gain value
|
||||
0); // pointer for gain computation
|
||||
|
||||
txa->amsq.p = AMSQ::create_amsq (
|
||||
0, // run
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->midbuff, // trigger buffer
|
||||
txa->dsp_rate, // sample rate
|
||||
0.010, // time constant for averaging signal
|
||||
0.004, // up-slew time
|
||||
0.004, // down-slew time
|
||||
0.180, // signal level to initiate tail
|
||||
0.200, // signal level to initiate unmute
|
||||
0.000, // minimum tail length
|
||||
0.025, // maximum tail length
|
||||
0.200); // muted gain
|
||||
|
||||
{
|
||||
double default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0};
|
||||
double default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0};
|
||||
//double default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
txa->eqp.p = EQP::create_eqp (
|
||||
0, // run - OFF by default
|
||||
txa->dsp_size, // size
|
||||
std::max(2048, txa->dsp_size), // number of filter coefficients
|
||||
0, // minimum phase flag
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
10, // nfreqs
|
||||
default_F, // vector of frequencies
|
||||
default_G, // vector of gain values
|
||||
0, // cutoff mode
|
||||
0, // wintype
|
||||
txa->dsp_rate); // samplerate
|
||||
}
|
||||
|
||||
txa->eqmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
&(txa->eqp.p->run), // pointer to eqp 'run'
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_EQ_AV, // index for average value
|
||||
TXA_EQ_PK, // index for peak value
|
||||
-1, // index for gain value
|
||||
0); // pointer for gain computation
|
||||
|
||||
txa->preemph.p = EMPHP::create_emphp (
|
||||
0, // run
|
||||
1, // position
|
||||
txa->dsp_size, // size
|
||||
std::max(2048, txa->dsp_size), // number of filter coefficients
|
||||
0, // minimum phase flag
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer,
|
||||
txa->dsp_rate, // sample rate
|
||||
0, // pre-emphasis type
|
||||
300.0, // f_low
|
||||
3000.0); // f_high
|
||||
|
||||
txa->leveler.p = WCPAGC::create_wcpagc (
|
||||
0, // run - OFF by default
|
||||
5, // mode
|
||||
0, // 0 for max(I,Q), 1 for envelope
|
||||
txa->midbuff, // input buff pointer
|
||||
txa->midbuff, // output buff pointer
|
||||
txa->dsp_size, // io_buffsize
|
||||
txa->dsp_rate, // sample rate
|
||||
0.001, // tau_attack
|
||||
0.500, // tau_decay
|
||||
6, // n_tau
|
||||
1.778, // max_gain
|
||||
1.0, // var_gain
|
||||
1.0, // fixed_gain
|
||||
1.0, // max_input
|
||||
1.05, // out_targ
|
||||
0.250, // tau_fast_backaverage
|
||||
0.005, // tau_fast_decay
|
||||
5.0, // pop_ratio
|
||||
0, // hang_enable
|
||||
0.500, // tau_hang_backmult
|
||||
0.500, // hangtime
|
||||
2.000, // hang_thresh
|
||||
0.100); // tau_hang_decay
|
||||
|
||||
txa->lvlrmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
&(txa->leveler.p->run), // pointer to leveler 'run'
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_LVLR_AV, // index for average value
|
||||
TXA_LVLR_PK, // index for peak value
|
||||
TXA_LVLR_GAIN, // index for gain value
|
||||
&txa->leveler.p->gain); // pointer for gain computation
|
||||
|
||||
{
|
||||
double default_F[5] = {200.0, 1000.0, 2000.0, 3000.0, 4000.0};
|
||||
double default_G[5] = {0.0, 5.0, 10.0, 10.0, 5.0};
|
||||
double default_E[5] = {7.0, 7.0, 7.0, 7.0, 7.0};
|
||||
txa->cfcomp.p = CFCOMP::create_cfcomp(
|
||||
0, // run
|
||||
0, // position
|
||||
0, // post-equalizer run
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
2048, // fft size
|
||||
4, // overlap
|
||||
txa->dsp_rate, // samplerate
|
||||
1, // window type
|
||||
0, // compression method
|
||||
5, // nfreqs
|
||||
0.0, // pre-compression
|
||||
0.0, // pre-postequalization
|
||||
default_F, // frequency array
|
||||
default_G, // compression array
|
||||
default_E, // eq array
|
||||
0.25, // metering time constant
|
||||
0.50); // display time constant
|
||||
}
|
||||
|
||||
txa->cfcmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
&(txa->cfcomp.p->run), // pointer to eqp 'run'
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_CFC_AV, // index for average value
|
||||
TXA_CFC_PK, // index for peak value
|
||||
TXA_CFC_GAIN, // index for gain value
|
||||
&txa->cfcomp.p->gain); // pointer for gain computation
|
||||
|
||||
txa->bp0.p = BANDPASS::create_bandpass (
|
||||
1, // always runs
|
||||
0, // position
|
||||
txa->dsp_size, // size
|
||||
std::max(2048, txa->dsp_size), // number of coefficients
|
||||
0, // flag for minimum phase
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
txa->f_low, // low freq cutoff
|
||||
txa->f_high, // high freq cutoff
|
||||
txa->dsp_rate, // samplerate
|
||||
1, // wintype
|
||||
2.0); // gain
|
||||
|
||||
txa->compressor.p = COMPRESSOR::create_compressor (
|
||||
0, // run - OFF by default
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
3.0); // gain
|
||||
|
||||
txa->bp1.p = BANDPASS::create_bandpass (
|
||||
0, // ONLY RUNS WHEN COMPRESSOR IS USED
|
||||
0, // position
|
||||
txa->dsp_size, // size
|
||||
std::max(2048, txa->dsp_size), // number of coefficients
|
||||
0, // flag for minimum phase
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
txa->f_low, // low freq cutoff
|
||||
txa->f_high, // high freq cutoff
|
||||
txa->dsp_rate, // samplerate
|
||||
1, // wintype
|
||||
2.0); // gain
|
||||
|
||||
txa->osctrl.p = OSCTRL::create_osctrl (
|
||||
0, // run
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->dsp_rate, // sample rate
|
||||
1.95); // gain for clippings
|
||||
|
||||
txa->bp2.p = BANDPASS::create_bandpass (
|
||||
0, // ONLY RUNS WHEN COMPRESSOR IS USED
|
||||
0, // position
|
||||
txa->dsp_size, // size
|
||||
std::max(2048, txa->dsp_size), // number of coefficients
|
||||
0, // flag for minimum phase
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
txa->f_low, // low freq cutoff
|
||||
txa->f_high, // high freq cutoff
|
||||
txa->dsp_rate, // samplerate
|
||||
1, // wintype
|
||||
1.0); // gain
|
||||
|
||||
txa->compmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
&(txa->compressor.p->run), // pointer to compressor 'run'
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_COMP_AV, // index for average value
|
||||
TXA_COMP_PK, // index for peak value
|
||||
-1, // index for gain value
|
||||
0); // pointer for gain computation
|
||||
|
||||
txa->alc.p = WCPAGC::create_wcpagc (
|
||||
1, // run - always ON
|
||||
5, // mode
|
||||
1, // 0 for max(I,Q), 1 for envelope
|
||||
txa->midbuff, // input buff pointer
|
||||
txa->midbuff, // output buff pointer
|
||||
txa->dsp_size, // io_buffsize
|
||||
txa->dsp_rate, // sample rate
|
||||
0.001, // tau_attack
|
||||
0.010, // tau_decay
|
||||
6, // n_tau
|
||||
1.0, // max_gain
|
||||
1.0, // var_gain
|
||||
1.0, // fixed_gain
|
||||
1.0, // max_input
|
||||
1.0, // out_targ
|
||||
0.250, // tau_fast_backaverage
|
||||
0.005, // tau_fast_decay
|
||||
5.0, // pop_ratio
|
||||
0, // hang_enable
|
||||
0.500, // tau_hang_backmult
|
||||
0.500, // hangtime
|
||||
2.000, // hang_thresh
|
||||
0.100); // tau_hang_decay
|
||||
|
||||
txa->ammod.p = AMMOD::create_ammod (
|
||||
0, // run - OFF by default
|
||||
0, // mode: 0=>AM, 1=>DSB
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to output buffer
|
||||
0.5); // carrier level
|
||||
|
||||
|
||||
txa->fmmod.p = FMMOD::create_fmmod (
|
||||
0, // run - OFF by default
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
5000.0, // deviation
|
||||
300.0, // low cutoff frequency
|
||||
3000.0, // high cutoff frequency
|
||||
1, // ctcss run control
|
||||
0.10, // ctcss level
|
||||
100.0, // ctcss frequency
|
||||
1, // run bandpass filter
|
||||
std::max(2048, txa->dsp_size), // number coefficients for bandpass filter
|
||||
0); // minimum phase flag
|
||||
|
||||
txa->gen1.p = GEN::create_gen (
|
||||
0, // run
|
||||
txa->dsp_size, // buffer size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->dsp_rate, // sample rate
|
||||
0); // mode
|
||||
|
||||
txa->uslew.p = USLEW::create_uslew (
|
||||
txa,
|
||||
&(txa->upslew), // pointer to channel upslew flag
|
||||
txa->dsp_size, // buffer size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->dsp_rate, // sample rate
|
||||
0.000, // delay time
|
||||
0.005); // upslew time
|
||||
|
||||
txa->alcmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
0, // optional pointer to a 'run'
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // pointer to buffer
|
||||
txa->dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_ALC_AV, // index for average value
|
||||
TXA_ALC_PK, // index for peak value
|
||||
TXA_ALC_GAIN, // index for gain value
|
||||
&txa->alc.p->gain); // pointer for gain computation
|
||||
|
||||
txa->sip1.p = SIPHON::create_siphon (
|
||||
1, // run
|
||||
0, // position
|
||||
0, // mode
|
||||
0, // disp
|
||||
txa->dsp_size, // input buffer size
|
||||
txa->midbuff, // input buffer
|
||||
16384, // number of samples to buffer
|
||||
16384, // fft size for spectrum
|
||||
1); // specmode
|
||||
|
||||
// txa->calcc.p = create_calcc (
|
||||
// channel, // channel number
|
||||
// 1, // run calibration
|
||||
// 1024, // input buffer size
|
||||
// txa->in_rate, // samplerate
|
||||
// 16, // ints
|
||||
// 256, // spi
|
||||
// (1.0 / 0.4072), // hw_scale
|
||||
// 0.1, // mox delay
|
||||
// 0.0, // loop delay
|
||||
// 0.8, // ptol
|
||||
// 0, // mox
|
||||
// 0, // solidmox
|
||||
// 1, // pin mode
|
||||
// 1, // map mode
|
||||
// 0, // stbl mode
|
||||
// 256, // pin samples
|
||||
// 0.9); // alpha
|
||||
|
||||
txa->iqc.p0 = txa->iqc.p1 = IQC::create_iqc (
|
||||
0, // run
|
||||
txa->dsp_size, // size
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
(double)txa->dsp_rate, // sample rate
|
||||
16, // ints
|
||||
0.005, // changeover time
|
||||
256); // spi
|
||||
|
||||
txa->cfir.p = CFIR::create_cfir(
|
||||
0, // run
|
||||
txa->dsp_size, // size
|
||||
std::max(2048, txa->dsp_size), // number of filter coefficients
|
||||
0, // minimum phase flag
|
||||
txa->midbuff, // input buffer
|
||||
txa->midbuff, // output buffer
|
||||
txa->dsp_rate, // input sample rate
|
||||
txa->out_rate, // CIC input sample rate
|
||||
1, // CIC differential delay
|
||||
640, // CIC interpolation factor
|
||||
5, // CIC integrator-comb pairs
|
||||
20000.0, // cutoff frequency
|
||||
2, // brick-wall windowed rolloff
|
||||
0.0, // raised-cosine transition width
|
||||
0); // window type
|
||||
|
||||
txa->rsmpout.p = RESAMPLE::create_resample (
|
||||
0, // run - will be turned ON below if needed
|
||||
txa->dsp_size, // input size
|
||||
txa->midbuff, // pointer to input buffer
|
||||
txa->outbuff, // pointer to output buffer
|
||||
txa->dsp_rate, // input sample rate
|
||||
txa->out_rate, // output sample rate
|
||||
0.0, // select cutoff automatically
|
||||
0, // select ncoef automatically
|
||||
0.980); // gain
|
||||
|
||||
txa->outmeter.p = METER::create_meter (
|
||||
1, // run
|
||||
0, // optional pointer to another 'run'
|
||||
txa->dsp_outsize, // size
|
||||
txa->outbuff, // pointer to buffer
|
||||
txa->out_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa->meter, // result vector
|
||||
txa->pmtupdate, // locks for meter access
|
||||
TXA_OUT_AV, // index for average value
|
||||
TXA_OUT_PK, // index for peak value
|
||||
-1, // index for gain value
|
||||
0); // pointer for gain computation
|
||||
|
||||
// turn OFF / ON resamplers as needed
|
||||
ResCheck (*txa);
|
||||
return txa;
|
||||
}
|
||||
|
||||
void TXA::destroy_txa (TXA *txa)
|
||||
{
|
||||
// in reverse order, free each item we created
|
||||
METER::destroy_meter (txa->outmeter.p);
|
||||
RESAMPLE::destroy_resample (txa->rsmpout.p);
|
||||
CFIR::destroy_cfir(txa->cfir.p);
|
||||
// destroy_calcc (txa->calcc.p);
|
||||
IQC::destroy_iqc (txa->iqc.p0);
|
||||
SIPHON::destroy_siphon (txa->sip1.p);
|
||||
METER::destroy_meter (txa->alcmeter.p);
|
||||
USLEW::destroy_uslew (txa->uslew.p);
|
||||
GEN::destroy_gen (txa->gen1.p);
|
||||
FMMOD::destroy_fmmod (txa->fmmod.p);
|
||||
AMMOD::destroy_ammod (txa->ammod.p);
|
||||
WCPAGC::destroy_wcpagc (txa->alc.p);
|
||||
METER::destroy_meter (txa->compmeter.p);
|
||||
BANDPASS::destroy_bandpass (txa->bp2.p);
|
||||
OSCTRL::destroy_osctrl (txa->osctrl.p);
|
||||
BANDPASS::destroy_bandpass (txa->bp1.p);
|
||||
COMPRESSOR::destroy_compressor (txa->compressor.p);
|
||||
BANDPASS::destroy_bandpass (txa->bp0.p);
|
||||
METER::destroy_meter (txa->cfcmeter.p);
|
||||
CFCOMP::destroy_cfcomp (txa->cfcomp.p);
|
||||
METER::destroy_meter (txa->lvlrmeter.p);
|
||||
WCPAGC::destroy_wcpagc (txa->leveler.p);
|
||||
EMPHP::destroy_emphp (txa->preemph.p);
|
||||
METER::destroy_meter (txa->eqmeter.p);
|
||||
EQP::destroy_eqp (txa->eqp.p);
|
||||
AMSQ::destroy_amsq (txa->amsq.p);
|
||||
METER::destroy_meter (txa->micmeter.p);
|
||||
PHROT::destroy_phrot (txa->phrot.p);
|
||||
PANEL::destroy_panel (txa->panel.p);
|
||||
GEN::destroy_gen (txa->gen0.p);
|
||||
RESAMPLE::destroy_resample (txa->rsmpin.p);
|
||||
delete[] (txa->midbuff);
|
||||
delete[] (txa->outbuff);
|
||||
delete[] (txa->inbuff);
|
||||
delete txa;
|
||||
}
|
||||
|
||||
void flush_txa (TXA* txa)
|
||||
{
|
||||
memset (txa->inbuff, 0, 1 * txa->dsp_insize * sizeof (dcomplex));
|
||||
memset (txa->outbuff, 0, 1 * txa->dsp_outsize * sizeof (dcomplex));
|
||||
memset (txa->midbuff, 0, 2 * txa->dsp_size * sizeof (dcomplex));
|
||||
RESAMPLE::flush_resample (txa->rsmpin.p);
|
||||
GEN::flush_gen (txa->gen0.p);
|
||||
PANEL::flush_panel (txa->panel.p);
|
||||
PHROT::flush_phrot (txa->phrot.p);
|
||||
METER::flush_meter (txa->micmeter.p);
|
||||
AMSQ::flush_amsq (txa->amsq.p);
|
||||
EQP::flush_eqp (txa->eqp.p);
|
||||
METER::flush_meter (txa->eqmeter.p);
|
||||
EMPHP::flush_emphp (txa->preemph.p);
|
||||
WCPAGC::flush_wcpagc (txa->leveler.p);
|
||||
METER::flush_meter (txa->lvlrmeter.p);
|
||||
CFCOMP::flush_cfcomp (txa->cfcomp.p);
|
||||
METER::flush_meter (txa->cfcmeter.p);
|
||||
BANDPASS::flush_bandpass (txa->bp0.p);
|
||||
COMPRESSOR::flush_compressor (txa->compressor.p);
|
||||
BANDPASS::flush_bandpass (txa->bp1.p);
|
||||
OSCTRL::flush_osctrl (txa->osctrl.p);
|
||||
BANDPASS::flush_bandpass (txa->bp2.p);
|
||||
METER::flush_meter (txa->compmeter.p);
|
||||
WCPAGC::flush_wcpagc (txa->alc.p);
|
||||
AMMOD::flush_ammod (txa->ammod.p);
|
||||
FMMOD::flush_fmmod (txa->fmmod.p);
|
||||
GEN::flush_gen (txa->gen1.p);
|
||||
USLEW::flush_uslew (txa->uslew.p);
|
||||
METER::flush_meter (txa->alcmeter.p);
|
||||
SIPHON::flush_siphon (txa->sip1.p);
|
||||
IQC::flush_iqc (txa->iqc.p0);
|
||||
CFIR::flush_cfir(txa->cfir.p);
|
||||
RESAMPLE::flush_resample (txa->rsmpout.p);
|
||||
METER::flush_meter (txa->outmeter.p);
|
||||
}
|
||||
|
||||
void xtxa (TXA* txa)
|
||||
{
|
||||
RESAMPLE::xresample (txa->rsmpin.p); // input resampler
|
||||
GEN::xgen (txa->gen0.p); // input signal generator
|
||||
PANEL::xpanel (txa->panel.p); // includes MIC gain
|
||||
PHROT::xphrot (txa->phrot.p); // phase rotator
|
||||
METER::xmeter (txa->micmeter.p); // MIC meter
|
||||
AMSQ::xamsqcap (txa->amsq.p); // downward expander capture
|
||||
AMSQ::xamsq (txa->amsq.p); // downward expander action
|
||||
EQP::xeqp (txa->eqp.p); // pre-EQ
|
||||
METER::xmeter (txa->eqmeter.p); // EQ meter
|
||||
EMPHP::xemphp (txa->preemph.p, 0); // FM pre-emphasis (first option)
|
||||
WCPAGC::xwcpagc (txa->leveler.p); // Leveler
|
||||
METER::xmeter (txa->lvlrmeter.p); // Leveler Meter
|
||||
CFCOMP::xcfcomp (txa->cfcomp.p, 0); // Continuous Frequency Compressor with post-EQ
|
||||
METER::xmeter (txa->cfcmeter.p); // CFC+PostEQ Meter
|
||||
BANDPASS::xbandpass (txa->bp0.p, 0); // primary bandpass filter
|
||||
COMPRESSOR::xcompressor (txa->compressor.p); // COMP compressor
|
||||
BANDPASS::xbandpass (txa->bp1.p, 0); // aux bandpass (runs if COMP)
|
||||
OSCTRL::xosctrl (txa->osctrl.p); // CESSB Overshoot Control
|
||||
BANDPASS::xbandpass (txa->bp2.p, 0); // aux bandpass (runs if CESSB)
|
||||
METER::xmeter (txa->compmeter.p); // COMP meter
|
||||
WCPAGC::xwcpagc (txa->alc.p); // ALC
|
||||
AMMOD::xammod (txa->ammod.p); // AM Modulator
|
||||
EMPHP::xemphp (txa->preemph.p, 1); // FM pre-emphasis (second option)
|
||||
FMMOD::xfmmod (txa->fmmod.p); // FM Modulator
|
||||
GEN::xgen (txa->gen1.p); // output signal generator (TUN and Two-tone)
|
||||
USLEW::xuslew (txa->uslew.p); // up-slew for AM, FM, and gens
|
||||
METER::xmeter (txa->alcmeter.p); // ALC Meter
|
||||
SIPHON::xsiphon (txa->sip1.p, 0); // siphon data for display
|
||||
IQC::xiqc (txa->iqc.p0); // PureSignal correction
|
||||
CFIR::xcfir(txa->cfir.p); // compensating FIR filter (used Protocol_2 only)
|
||||
RESAMPLE::xresample (txa->rsmpout.p); // output resampler
|
||||
METER::xmeter (txa->outmeter.p); // output meter
|
||||
// print_peak_env ("env_exception.txt", txa->dsp_outsize, txa->outbuff, 0.7);
|
||||
}
|
||||
|
||||
void TXA::setInputSamplerate (TXA *txa, int dsp_insize, int in_rate)
|
||||
{
|
||||
txa->csDSP.lock();
|
||||
txa->dsp_insize = dsp_insize;
|
||||
txa->in_rate = in_rate;
|
||||
// buffers
|
||||
delete[] (txa->inbuff);
|
||||
txa->inbuff = new double[1 * txa->dsp_insize * 2]; //(double *)malloc0(1 * txa->dsp_insize * sizeof(complex));
|
||||
// input resampler
|
||||
RESAMPLE::setBuffers_resample (txa->rsmpin.p, txa->inbuff, txa->midbuff);
|
||||
RESAMPLE::setSize_resample (txa->rsmpin.p, txa->dsp_insize);
|
||||
RESAMPLE::setInRate_resample (txa->rsmpin.p, txa->in_rate);
|
||||
ResCheck (*txa);
|
||||
txa->csDSP.unlock();
|
||||
}
|
||||
|
||||
void TXA::setOutputSamplerate (TXA* txa, int dsp_outsize, int out_rate)
|
||||
{
|
||||
txa->csDSP.lock();
|
||||
txa->dsp_outsize = dsp_outsize;
|
||||
txa->out_rate = out_rate;
|
||||
// buffers
|
||||
delete[] (txa->outbuff);
|
||||
txa->outbuff = new double[1 * txa->dsp_outsize * 2]; // (double *)malloc0(1 * txa->dsp_outsize * sizeof(complex));
|
||||
// cfir - needs to know input rate of firmware CIC
|
||||
CFIR::setOutRate_cfir (txa->cfir.p, txa->out_rate);
|
||||
// output resampler
|
||||
RESAMPLE::setBuffers_resample (txa->rsmpout.p, txa->midbuff, txa->outbuff);
|
||||
RESAMPLE::setOutRate_resample (txa->rsmpout.p, txa->out_rate);
|
||||
ResCheck (*txa);
|
||||
// output meter
|
||||
METER::setBuffers_meter (txa->outmeter.p, txa->outbuff);
|
||||
METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize);
|
||||
METER::setSamplerate_meter (txa->outmeter.p, txa->out_rate);
|
||||
txa->csDSP.unlock();
|
||||
}
|
||||
|
||||
void TXA::setDSPSamplerate (TXA *txa, int dsp_insize, int dsp_outsize, int dsp_rate)
|
||||
{
|
||||
txa->csDSP.lock();
|
||||
txa->dsp_insize = dsp_insize;
|
||||
txa->dsp_outsize = dsp_outsize;
|
||||
txa->dsp_rate = dsp_rate;
|
||||
// buffers
|
||||
delete[] (txa->inbuff);
|
||||
txa->inbuff = new double[1 * txa->dsp_insize * 2]; // (double *)malloc0(1 * txa->dsp_insize * sizeof(complex));
|
||||
delete[] (txa->outbuff);
|
||||
txa->outbuff = new double[1 * txa->dsp_outsize * 2]; // (double *)malloc0(1 * txa->dsp_outsize * sizeof(complex));
|
||||
// input resampler
|
||||
RESAMPLE::setBuffers_resample (txa->rsmpin.p, txa->inbuff, txa->midbuff);
|
||||
RESAMPLE::setSize_resample (txa->rsmpin.p, txa->dsp_insize);
|
||||
RESAMPLE::setOutRate_resample (txa->rsmpin.p, txa->dsp_rate);
|
||||
// dsp_rate blocks
|
||||
GEN::setSamplerate_gen (txa->gen0.p, txa->dsp_rate);
|
||||
PANEL::setSamplerate_panel (txa->panel.p, txa->dsp_rate);
|
||||
PHROT::setSamplerate_phrot (txa->phrot.p, txa->dsp_rate);
|
||||
METER::setSamplerate_meter (txa->micmeter.p, txa->dsp_rate);
|
||||
AMSQ::setSamplerate_amsq (txa->amsq.p, txa->dsp_rate);
|
||||
EQP::setSamplerate_eqp (txa->eqp.p, txa->dsp_rate);
|
||||
METER::setSamplerate_meter (txa->eqmeter.p, txa->dsp_rate);
|
||||
EMPHP::setSamplerate_emphp (txa->preemph.p, txa->dsp_rate);
|
||||
WCPAGC::setSamplerate_wcpagc (txa->leveler.p, txa->dsp_rate);
|
||||
METER::setSamplerate_meter (txa->lvlrmeter.p, txa->dsp_rate);
|
||||
CFCOMP::setSamplerate_cfcomp (txa->cfcomp.p, txa->dsp_rate);
|
||||
METER::setSamplerate_meter (txa->cfcmeter.p, txa->dsp_rate);
|
||||
BANDPASS::setSamplerate_bandpass (txa->bp0.p, txa->dsp_rate);
|
||||
COMPRESSOR::setSamplerate_compressor (txa->compressor.p, txa->dsp_rate);
|
||||
BANDPASS::setSamplerate_bandpass (txa->bp1.p, txa->dsp_rate);
|
||||
OSCTRL::setSamplerate_osctrl (txa->osctrl.p, txa->dsp_rate);
|
||||
BANDPASS::setSamplerate_bandpass (txa->bp2.p, txa->dsp_rate);
|
||||
METER::setSamplerate_meter (txa->compmeter.p, txa->dsp_rate);
|
||||
WCPAGC::setSamplerate_wcpagc (txa->alc.p, txa->dsp_rate);
|
||||
AMMOD::setSamplerate_ammod (txa->ammod.p, txa->dsp_rate);
|
||||
FMMOD::setSamplerate_fmmod (txa->fmmod.p, txa->dsp_rate);
|
||||
GEN::setSamplerate_gen (txa->gen1.p, txa->dsp_rate);
|
||||
USLEW::setSamplerate_uslew (txa->uslew.p, txa->dsp_rate);
|
||||
METER::setSamplerate_meter (txa->alcmeter.p, txa->dsp_rate);
|
||||
SIPHON::setSamplerate_siphon (txa->sip1.p, txa->dsp_rate);
|
||||
IQC::setSamplerate_iqc (txa->iqc.p0, txa->dsp_rate);
|
||||
CFIR::setSamplerate_cfir (txa->cfir.p, txa->dsp_rate);
|
||||
// output resampler
|
||||
RESAMPLE::setBuffers_resample (txa->rsmpout.p, txa->midbuff, txa->outbuff);
|
||||
RESAMPLE::setInRate_resample (txa->rsmpout.p, txa->dsp_rate);
|
||||
ResCheck (*txa);
|
||||
// output meter
|
||||
METER::setBuffers_meter (txa->outmeter.p, txa->outbuff);
|
||||
METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize);
|
||||
txa->csDSP.unlock();
|
||||
}
|
||||
|
||||
void TXA::setDSPBuffsize (TXA *txa, int dsp_insize, int dsp_size, int dsp_outsize)
|
||||
{
|
||||
txa->csDSP.lock();
|
||||
txa->dsp_insize = dsp_insize;
|
||||
txa->dsp_size = dsp_size;
|
||||
txa->dsp_outsize = dsp_outsize;
|
||||
// buffers
|
||||
delete[] (txa->inbuff);
|
||||
txa->inbuff = new double[1 * txa->dsp_insize * 2]; // (double *)malloc0(1 * txa->dsp_insize * sizeof(complex));
|
||||
delete[] (txa->midbuff);
|
||||
txa->midbuff = new double[2 * txa->dsp_size * 2]; // (double *)malloc0(2 * txa->dsp_size * sizeof(complex));
|
||||
delete[] (txa->outbuff);
|
||||
txa->outbuff = new double[1 * txa->dsp_outsize * 2]; // (double *)malloc0(1 * txa->dsp_outsize * sizeof(complex));
|
||||
// input resampler
|
||||
RESAMPLE::setBuffers_resample (txa->rsmpin.p, txa->inbuff, txa->midbuff);
|
||||
RESAMPLE::setSize_resample (txa->rsmpin.p, txa->dsp_insize);
|
||||
// dsp_size blocks
|
||||
GEN::setBuffers_gen (txa->gen0.p, txa->midbuff, txa->midbuff);
|
||||
GEN::setSize_gen (txa->gen0.p, txa->dsp_size);
|
||||
PANEL::setBuffers_panel (txa->panel.p, txa->midbuff, txa->midbuff);
|
||||
PANEL::setSize_panel (txa->panel.p, txa->dsp_size);
|
||||
PHROT::setBuffers_phrot (txa->phrot.p, txa->midbuff, txa->midbuff);
|
||||
PHROT::setSize_phrot (txa->phrot.p, txa->dsp_size);
|
||||
METER::setBuffers_meter (txa->micmeter.p, txa->midbuff);
|
||||
METER::setSize_meter (txa->micmeter.p, txa->dsp_size);
|
||||
AMSQ::setBuffers_amsq (txa->amsq.p, txa->midbuff, txa->midbuff, txa->midbuff);
|
||||
AMSQ::setSize_amsq (txa->amsq.p, txa->dsp_size);
|
||||
EQP::setBuffers_eqp (txa->eqp.p, txa->midbuff, txa->midbuff);
|
||||
EQP::setSize_eqp (txa->eqp.p, txa->dsp_size);
|
||||
METER::setBuffers_meter (txa->eqmeter.p, txa->midbuff);
|
||||
METER::setSize_meter (txa->eqmeter.p, txa->dsp_size);
|
||||
EMPHP::setBuffers_emphp (txa->preemph.p, txa->midbuff, txa->midbuff);
|
||||
EMPHP::setSize_emphp (txa->preemph.p, txa->dsp_size);
|
||||
WCPAGC::setBuffers_wcpagc (txa->leveler.p, txa->midbuff, txa->midbuff);
|
||||
WCPAGC::setSize_wcpagc (txa->leveler.p, txa->dsp_size);
|
||||
METER::setBuffers_meter (txa->lvlrmeter.p, txa->midbuff);
|
||||
METER::setSize_meter (txa->lvlrmeter.p, txa->dsp_size);
|
||||
CFCOMP::setBuffers_cfcomp (txa->cfcomp.p, txa->midbuff, txa->midbuff);
|
||||
CFCOMP::setSize_cfcomp (txa->cfcomp.p, txa->dsp_size);
|
||||
METER::setBuffers_meter (txa->cfcmeter.p, txa->midbuff);
|
||||
METER::setSize_meter (txa->cfcmeter.p, txa->dsp_size);
|
||||
BANDPASS::setBuffers_bandpass (txa->bp0.p, txa->midbuff, txa->midbuff);
|
||||
BANDPASS::setSize_bandpass (txa->bp0.p, txa->dsp_size);
|
||||
COMPRESSOR::setBuffers_compressor (txa->compressor.p, txa->midbuff, txa->midbuff);
|
||||
COMPRESSOR::setSize_compressor (txa->compressor.p, txa->dsp_size);
|
||||
BANDPASS::setBuffers_bandpass (txa->bp1.p, txa->midbuff, txa->midbuff);
|
||||
BANDPASS::setSize_bandpass (txa->bp1.p, txa->dsp_size);
|
||||
OSCTRL::setBuffers_osctrl (txa->osctrl.p, txa->midbuff, txa->midbuff);
|
||||
OSCTRL::setSize_osctrl (txa->osctrl.p, txa->dsp_size);
|
||||
BANDPASS::setBuffers_bandpass (txa->bp2.p, txa->midbuff, txa->midbuff);
|
||||
BANDPASS::setSize_bandpass (txa->bp2.p, txa->dsp_size);
|
||||
METER::setBuffers_meter (txa->compmeter.p, txa->midbuff);
|
||||
METER::setSize_meter (txa->compmeter.p, txa->dsp_size);
|
||||
WCPAGC::setBuffers_wcpagc (txa->alc.p, txa->midbuff, txa->midbuff);
|
||||
WCPAGC::setSize_wcpagc (txa->alc.p, txa->dsp_size);
|
||||
AMMOD::setBuffers_ammod (txa->ammod.p, txa->midbuff, txa->midbuff);
|
||||
AMMOD::setSize_ammod (txa->ammod.p, txa->dsp_size);
|
||||
FMMOD::setBuffers_fmmod (txa->fmmod.p, txa->midbuff, txa->midbuff);
|
||||
FMMOD::setSize_fmmod (txa->fmmod.p, txa->dsp_size);
|
||||
GEN::setBuffers_gen (txa->gen1.p, txa->midbuff, txa->midbuff);
|
||||
GEN::setSize_gen (txa->gen1.p, txa->dsp_size);
|
||||
USLEW::setBuffers_uslew (txa->uslew.p, txa->midbuff, txa->midbuff);
|
||||
USLEW::setSize_uslew (txa->uslew.p, txa->dsp_size);
|
||||
METER::setBuffers_meter (txa->alcmeter.p, txa->midbuff);
|
||||
METER::setSize_meter (txa->alcmeter.p, txa->dsp_size);
|
||||
SIPHON::setBuffers_siphon (txa->sip1.p, txa->midbuff);
|
||||
SIPHON::setSize_siphon (txa->sip1.p, txa->dsp_size);
|
||||
IQC::setBuffers_iqc (txa->iqc.p0, txa->midbuff, txa->midbuff);
|
||||
IQC::setSize_iqc (txa->iqc.p0, txa->dsp_size);
|
||||
CFIR::setBuffers_cfir (txa->cfir.p, txa->midbuff, txa->midbuff);
|
||||
CFIR::setSize_cfir (txa->cfir.p, txa->dsp_size);
|
||||
// output resampler
|
||||
RESAMPLE::setBuffers_resample (txa->rsmpout.p, txa->midbuff, txa->outbuff);
|
||||
RESAMPLE::setSize_resample (txa->rsmpout.p, txa->dsp_size);
|
||||
// output meter
|
||||
METER::setBuffers_meter (txa->outmeter.p, txa->outbuff);
|
||||
METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize);
|
||||
txa->csDSP.unlock();
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void TXA::SetMode (TXA& txa, int mode)
|
||||
{
|
||||
if (txa.mode != mode)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.mode = mode;
|
||||
txa.ammod.p->run = 0;
|
||||
txa.fmmod.p->run = 0;
|
||||
txa.preemph.p->run = 0;
|
||||
switch (mode)
|
||||
{
|
||||
case TXA_AM:
|
||||
case TXA_SAM:
|
||||
txa.ammod.p->run = 1;
|
||||
txa.ammod.p->mode = 0;
|
||||
break;
|
||||
case TXA_DSB:
|
||||
txa.ammod.p->run = 1;
|
||||
txa.ammod.p->mode = 1;
|
||||
break;
|
||||
case TXA_AM_LSB:
|
||||
case TXA_AM_USB:
|
||||
txa.ammod.p->run = 1;
|
||||
txa.ammod.p->mode = 2;
|
||||
break;
|
||||
case TXA_FM:
|
||||
txa.fmmod.p->run = 1;
|
||||
txa.preemph.p->run = 1;
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
SetupBPFilters (txa);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void TXA::SetBandpassFreqs (TXA& txa, double f_low, double f_high)
|
||||
{
|
||||
if ((txa.f_low != f_low) || (txa.f_high != f_high))
|
||||
{
|
||||
txa.f_low = f_low;
|
||||
txa.f_high = f_high;
|
||||
SetupBPFilters (txa);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Internal Functions *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void TXA::ResCheck (TXA& txa)
|
||||
{
|
||||
RESAMPLE *a = txa.rsmpin.p;
|
||||
if (txa.in_rate != txa.dsp_rate)
|
||||
a->run = 1;
|
||||
else
|
||||
a->run = 0;
|
||||
a = txa.rsmpout.p;
|
||||
if (txa.dsp_rate != txa.out_rate)
|
||||
a->run = 1;
|
||||
else
|
||||
a->run = 0;
|
||||
}
|
||||
|
||||
int TXA::UslewCheck (TXA& txa)
|
||||
{
|
||||
return (txa.ammod.p->run == 1) ||
|
||||
(txa.fmmod.p->run == 1) ||
|
||||
(txa.gen0.p->run == 1) ||
|
||||
(txa.gen1.p->run == 1);
|
||||
}
|
||||
|
||||
void TXA::SetupBPFilters (TXA& txa)
|
||||
{
|
||||
txa.bp0.p->run = 1;
|
||||
txa.bp1.p->run = 0;
|
||||
txa.bp2.p->run = 0;
|
||||
switch (txa.mode)
|
||||
{
|
||||
case TXA_LSB:
|
||||
case TXA_USB:
|
||||
case TXA_CWL:
|
||||
case TXA_CWU:
|
||||
case TXA_DIGL:
|
||||
case TXA_DIGU:
|
||||
case TXA_SPEC:
|
||||
case TXA_DRM:
|
||||
BANDPASS::CalcBandpassFilter (txa.bp0.p, txa.f_low, txa.f_high, 2.0);
|
||||
if (txa.compressor.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp1.p, txa.f_low, txa.f_high, 2.0);
|
||||
txa.bp1.p->run = 1;
|
||||
if (txa.osctrl.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp2.p, txa.f_low, txa.f_high, 1.0);
|
||||
txa.bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TXA_DSB:
|
||||
case TXA_AM:
|
||||
case TXA_SAM:
|
||||
case TXA_FM:
|
||||
if (txa.compressor.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp0.p, 0.0, txa.f_high, 2.0);
|
||||
BANDPASS::CalcBandpassFilter (txa.bp1.p, 0.0, txa.f_high, 2.0);
|
||||
txa.bp1.p->run = 1;
|
||||
if (txa.osctrl.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp2.p, 0.0, txa.f_high, 1.0);
|
||||
txa.bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp0.p, txa.f_low, txa.f_high, 1.0);
|
||||
}
|
||||
break;
|
||||
case TXA_AM_LSB:
|
||||
BANDPASS::CalcBandpassFilter (txa.bp0.p, -txa.f_high, 0.0, 2.0);
|
||||
if (txa.compressor.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp1.p, -txa.f_high, 0.0, 2.0);
|
||||
txa.bp1.p->run = 1;
|
||||
if (txa.osctrl.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp2.p, -txa.f_high, 0.0, 1.0);
|
||||
txa.bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TXA_AM_USB:
|
||||
BANDPASS::CalcBandpassFilter (txa.bp0.p, 0.0, txa.f_high, 2.0);
|
||||
if (txa.compressor.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp1.p, 0.0, txa.f_high, 2.0);
|
||||
txa.bp1.p->run = 1;
|
||||
if (txa.osctrl.p->run)
|
||||
{
|
||||
BANDPASS::CalcBandpassFilter (txa.bp2.p, 0.0, txa.f_high, 1.0);
|
||||
txa.bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Collectives *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void TXA::SetNC (TXA& txa, int nc)
|
||||
{
|
||||
int oldstate = txa.state;
|
||||
BANDPASS::SetBandpassNC (txa, nc);
|
||||
EMPHP::SetFMEmphNC (txa, nc);
|
||||
EQP::SetEQNC (txa, nc);
|
||||
FMMOD::SetFMNC (txa, nc);
|
||||
CFIR::SetCFIRNC (txa, nc);
|
||||
txa.state = oldstate;
|
||||
}
|
||||
|
||||
void TXA::SetMP (TXA& txa, int mp)
|
||||
{
|
||||
BANDPASS::SetBandpassMP (txa, mp);
|
||||
EMPHP::SetFMEmphMP (txa, mp);
|
||||
EQP::SetEQMP (txa, mp);
|
||||
FMMOD::SetFMMP (txa, mp);
|
||||
}
|
||||
|
||||
void TXA::SetFMAFFilter (TXA& txa, double low, double high)
|
||||
{
|
||||
EMPHP::SetFMPreEmphFreqs (txa, low, high);
|
||||
FMMOD::SetFMAFFreqs (txa, low, high);
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
258
wdsp/TXA.hpp
Normal file
258
wdsp/TXA.hpp
Normal file
@ -0,0 +1,258 @@
|
||||
/* TXA.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_txa_h
|
||||
#define wdsp_txa_h
|
||||
|
||||
#include <atomic>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "unit.hpp"
|
||||
|
||||
#include "ammod.hpp"
|
||||
#include "meter.hpp"
|
||||
#include "resample.hpp"
|
||||
#include "patchpanel.hpp"
|
||||
#include "amsq.hpp"
|
||||
#include "eq.hpp"
|
||||
#include "iir.hpp"
|
||||
#include "cfcomp.hpp"
|
||||
#include "compress.hpp"
|
||||
#include "bandpass.hpp"
|
||||
#include "bps.hpp"
|
||||
#include "osctrl.hpp"
|
||||
#include "wcpAGC.hpp"
|
||||
#include "emph.hpp"
|
||||
#include "fmmod.hpp"
|
||||
#include "siphon.hpp"
|
||||
#include "gen.hpp"
|
||||
#include "slew.hpp"
|
||||
// #include "calcc.h"
|
||||
#include "iqc.hpp"
|
||||
#include "cfir.hpp"
|
||||
#include "export.h"
|
||||
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class METER;
|
||||
class RESAMPLE;
|
||||
class PANEL;
|
||||
class AMSQ;
|
||||
class EQP;
|
||||
class PHROT;
|
||||
class CFCOMP;
|
||||
class COMPRESSOR;
|
||||
class BANDPASS;
|
||||
class BPS;
|
||||
class OSCTRL;
|
||||
class WCPAGC;
|
||||
class AMMOD;
|
||||
class EMPHP;
|
||||
class FMMOD;
|
||||
class SIPHON;
|
||||
class GEN;
|
||||
class USLEW;
|
||||
class IQC;
|
||||
class CFIR;
|
||||
|
||||
class WDSP_API TXA : public Unit
|
||||
{
|
||||
public:
|
||||
enum txaMode
|
||||
{
|
||||
TXA_LSB,
|
||||
TXA_USB,
|
||||
TXA_DSB,
|
||||
TXA_CWL,
|
||||
TXA_CWU,
|
||||
TXA_FM,
|
||||
TXA_AM,
|
||||
TXA_DIGU,
|
||||
TXA_SPEC,
|
||||
TXA_DIGL,
|
||||
TXA_SAM,
|
||||
TXA_DRM,
|
||||
TXA_AM_LSB,
|
||||
TXA_AM_USB
|
||||
};
|
||||
|
||||
enum txaMeterType
|
||||
{
|
||||
TXA_MIC_PK,
|
||||
TXA_MIC_AV,
|
||||
TXA_EQ_PK,
|
||||
TXA_EQ_AV,
|
||||
TXA_LVLR_PK,
|
||||
TXA_LVLR_AV,
|
||||
TXA_LVLR_GAIN,
|
||||
TXA_CFC_PK,
|
||||
TXA_CFC_AV,
|
||||
TXA_CFC_GAIN,
|
||||
TXA_COMP_PK,
|
||||
TXA_COMP_AV,
|
||||
TXA_ALC_PK,
|
||||
TXA_ALC_AV,
|
||||
TXA_ALC_GAIN,
|
||||
TXA_OUT_PK,
|
||||
TXA_OUT_AV,
|
||||
TXA_METERTYPE_LAST
|
||||
};
|
||||
|
||||
double* inbuff;
|
||||
double* outbuff;
|
||||
double* midbuff;
|
||||
int mode;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double meter[TXA_METERTYPE_LAST];
|
||||
QRecursiveMutex *pmtupdate[TXA_METERTYPE_LAST];
|
||||
std::atomic<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;
|
||||
struct
|
||||
{
|
||||
IQC *p0, *p1;
|
||||
// p0 for dsp-synchronized reference, p1 for other
|
||||
} iqc;
|
||||
struct
|
||||
{
|
||||
CFIR *p;
|
||||
} cfir;
|
||||
|
||||
static TXA* create_txa (
|
||||
int in_rate, // input samplerate
|
||||
int out_rate, // output samplerate
|
||||
int in_size, // input buffsize (complex samples) in a fexchange() operation
|
||||
int dsp_rate, // sample rate for mainstream dsp processing
|
||||
int dsp_size, // number complex samples processed per buffer in mainstream dsp processing
|
||||
int dsp_insize, // size (complex samples) of the output of the r1 (input) buffer
|
||||
int dsp_outsize, // size (complex samples) of the input of the r2 (output) buffer
|
||||
int out_size // output buffsize (complex samples) in a fexchange() operation
|
||||
);
|
||||
static void destroy_txa (TXA *txa);
|
||||
static void flush_txa (TXA *txa);
|
||||
static void xtxa (TXA *txa);
|
||||
static void setInputSamplerate (TXA *txa, int dsp_insize, int in_rate);
|
||||
static void setOutputSamplerate (TXA *txa, int dsp_outsize, int out_rate);
|
||||
static void setDSPSamplerate (TXA *txa, int dsp_insize, int dsp_outsize, int dsp_rate);
|
||||
static void setDSPBuffsize (TXA *txa, int dsp_insize, int dsp_size, int dsp_outsize);
|
||||
|
||||
// TXA Properties
|
||||
static void SetMode (TXA& txa, int mode);
|
||||
static void SetBandpassFreqs (TXA& txa, double f_low, double f_high);
|
||||
|
||||
// Collectives
|
||||
static void SetNC (TXA& txa, int nc);
|
||||
static void SetMP (TXA& txa, int mp);
|
||||
static void SetFMAFFilter (TXA& txa, double low, double high);
|
||||
static void SetupBPFilters (TXA& txa);
|
||||
static int UslewCheck (TXA& txa);
|
||||
|
||||
private:
|
||||
static void ResCheck (TXA& txa);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
305
wdsp/amd.cpp
Normal file
305
wdsp/amd.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
/* amd.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 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 <cmath>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "amd.hpp"
|
||||
#include "anf.hpp"
|
||||
#include "emnr.hpp"
|
||||
#include "anr.hpp"
|
||||
#include "snb.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
AMD* AMD::create_amd
|
||||
(
|
||||
int run,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *out_buff,
|
||||
int mode,
|
||||
int levelfade,
|
||||
int sbmode,
|
||||
int sample_rate,
|
||||
double fmin,
|
||||
double fmax,
|
||||
double zeta,
|
||||
double omegaN,
|
||||
double tauR,
|
||||
double tauI
|
||||
)
|
||||
{
|
||||
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 = (double)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;
|
||||
}
|
||||
|
||||
void AMD::destroy_amd(AMD *a)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
void AMD::init_amd(AMD *a)
|
||||
{
|
||||
//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;
|
||||
|
||||
//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;
|
||||
|
||||
//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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void AMD::flush_amd (AMD *a)
|
||||
{
|
||||
a->dc = 0.0;
|
||||
a->dc_insert = 0.0;
|
||||
}
|
||||
|
||||
void AMD::xamd (AMD *a)
|
||||
{
|
||||
int i;
|
||||
double audio;
|
||||
double vco[2];
|
||||
double corr[2];
|
||||
double det;
|
||||
double del_out;
|
||||
double ai, bi, aq, bq;
|
||||
double ai_ps, bi_ps, aq_ps, bq_ps;
|
||||
int j, k;
|
||||
if (a->run)
|
||||
{
|
||||
switch (a->mode)
|
||||
{
|
||||
|
||||
case 0: //AM Demodulator
|
||||
{
|
||||
for (i = 0; i < a->buff_size; i++)
|
||||
{
|
||||
audio = sqrt(a->in_buff[2 * i + 0] * a->in_buff[2 * i + 0] + a->in_buff[2 * i + 1] * a->in_buff[2 * i + 1]);
|
||||
if (a->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;
|
||||
}
|
||||
a->out_buff[2 * i + 0] = audio;
|
||||
a->out_buff[2 * i + 1] = audio;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: //Synchronous AM Demodulator with Sideband Separation
|
||||
{
|
||||
for (i = 0; i < a->buff_size; i++)
|
||||
{
|
||||
vco[0] = cos(a->phs);
|
||||
vco[1] = sin(a->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];
|
||||
|
||||
if (a->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;
|
||||
|
||||
for (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];
|
||||
}
|
||||
ai_ps = a->a[OUT_IDX];
|
||||
bi_ps = a->b[OUT_IDX];
|
||||
bq_ps = a->c[OUT_IDX];
|
||||
aq_ps = a->d[OUT_IDX];
|
||||
|
||||
for (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];
|
||||
}
|
||||
}
|
||||
|
||||
corr[0] = +ai + bq;
|
||||
corr[1] = -bi + aq;
|
||||
|
||||
switch(a->sbmode)
|
||||
{
|
||||
case 0: //both sidebands
|
||||
{
|
||||
audio = corr[0];
|
||||
break;
|
||||
}
|
||||
case 1: //LSB
|
||||
{
|
||||
audio = (ai_ps - bi_ps) + (aq_ps + bq_ps);
|
||||
break;
|
||||
}
|
||||
case 2: //USB
|
||||
{
|
||||
audio = (ai_ps + bi_ps) - (aq_ps - bq_ps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->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;
|
||||
}
|
||||
a->out_buff[2 * i + 0] = audio;
|
||||
a->out_buff[2 * i + 1] = 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;
|
||||
if (a->omega < a->omega_min) a->omega = a->omega_min;
|
||||
if (a->omega > a->omega_max) a->omega = a->omega_max;
|
||||
a->fil_out = a->g1 * det + a->omega;
|
||||
a->phs += del_out;
|
||||
while (a->phs >= 2 * M_PI) a->phs -= 2 * M_PI;
|
||||
while (a->phs < 0.0) a->phs += 2 * M_PI;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->in_buff != a->out_buff)
|
||||
{
|
||||
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof(dcomplex));
|
||||
}
|
||||
}
|
||||
|
||||
void AMD::setBuffers_amd (AMD *a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void AMD::setSamplerate_amd (AMD *a, int rate)
|
||||
{
|
||||
a->sample_rate = rate;
|
||||
init_amd(a);
|
||||
}
|
||||
|
||||
void AMD::setSize_amd (AMD *a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void AMD::SetAMDRun(RXA& rxa, int run)
|
||||
{
|
||||
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);
|
||||
rxa.csDSP.lock();
|
||||
a->run = run;
|
||||
RXA::bp1Set (rxa);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void AMD::SetAMDSBMode(RXA& rxa, int sbmode)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.amd.p->sbmode = sbmode;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void AMD::SetAMDFadeLevel(RXA& rxa, int levelfade)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.amd.p->levelfade = levelfade;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namesoace WDSP
|
||||
116
wdsp/amd.hpp
Normal file
116
wdsp/amd.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/* amd.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#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 "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API AMD {
|
||||
public:
|
||||
int run;
|
||||
int buff_size; // buffer size
|
||||
double *in_buff; // pointer to input buffer
|
||||
double *out_buff; // pointer to output buffer
|
||||
int mode; // demodulation mode
|
||||
double sample_rate; // sample rate
|
||||
double dc; // dc component in demodulated output
|
||||
double fmin; // pll - minimum carrier freq to lock
|
||||
double fmax; // pll - maximum carrier freq to lock
|
||||
double omega_min; // pll - minimum lock check parameter
|
||||
double omega_max; // pll - maximum lock check parameter
|
||||
double zeta; // pll - damping factor; as coded, must be <=1.0
|
||||
double omegaN; // pll - natural frequency
|
||||
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 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
|
||||
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
|
||||
(
|
||||
int run,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *out_buff,
|
||||
int mode,
|
||||
int levelfade,
|
||||
int sbmode,
|
||||
int sample_rate,
|
||||
double fmin,
|
||||
double fmax,
|
||||
double zeta,
|
||||
double omegaN,
|
||||
double tauR,
|
||||
double tauI
|
||||
);
|
||||
|
||||
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, double* in, double* 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);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
119
wdsp/ammod.cpp
Normal file
119
wdsp/ammod.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/* ammod.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 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 <cmath>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "ammod.hpp"
|
||||
#include "comm.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
AMMOD* AMMOD::create_ammod (int run, int mode, int size, double* in, double* 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;
|
||||
}
|
||||
|
||||
void AMMOD::destroy_ammod(AMMOD *a)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
void AMMOD::flush_ammod(AMMOD *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AMMOD::xammod(AMMOD *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
switch (a->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]);
|
||||
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];
|
||||
break;
|
||||
case 2: // SSB w/Carrier
|
||||
for (i = 0; i < a->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];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void AMMOD::setBuffers_ammod(AMMOD *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void AMMOD::setSamplerate_ammod(AMMOD *, int)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AMMOD::setSize_ammod(AMMOD *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void AMMOD::SetAMCarrierLevel (TXA& txa, double c_level)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.ammod.p->c_level = c_level;
|
||||
txa.ammod.p->a_level = 1.0 - c_level;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
62
wdsp/ammod.hpp
Normal file
62
wdsp/ammod.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
/* ammod.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_ammod_h
|
||||
#define wdsp_ammod_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API AMMOD
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int mode;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double c_level;
|
||||
double a_level;
|
||||
double mult;
|
||||
|
||||
static AMMOD* create_ammod(int run, int mode, int size, double* in, double* out, double 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, double* in, double* out);
|
||||
static void setSamplerate_ammod (AMMOD *a, int rate);
|
||||
static void setSize_ammod (AMMOD *a, int size);
|
||||
// TXA Properties
|
||||
static void SetAMCarrierLevel (TXA& txa, double c_level);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
276
wdsp/amsq.cpp
Normal file
276
wdsp/amsq.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
/* amsq.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 <QRecursiveMutex>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "amsq.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void AMSQ::compute_slews(AMSQ *a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
delta = PI / (double)a->ntup;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntup; i++)
|
||||
{
|
||||
a->cup[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 - cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
delta = PI / (double)a->ntdown;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntdown; i++)
|
||||
{
|
||||
a->cdown[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 + cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void AMSQ::calc_amsq(AMSQ *a)
|
||||
{
|
||||
// signal averaging
|
||||
a->trigsig = new double[a->size * 2]; // (double *)malloc0(a->size * sizeof(dcomplex));
|
||||
a->avm = exp(-1.0 / (a->rate * a->avtau));
|
||||
a->onem_avm = 1.0 - a->avm;
|
||||
a->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]; // (double *)malloc0((a->ntup + 1) * sizeof(double));
|
||||
a->cdown = new double[(a->ntdown + 1) * 2]; // (double *)malloc0((a->ntdown + 1) * sizeof(double));
|
||||
compute_slews(a);
|
||||
// control
|
||||
a->state = 0;
|
||||
}
|
||||
|
||||
void AMSQ::decalc_amsq (AMSQ *a)
|
||||
{
|
||||
delete[] a->cdown;
|
||||
delete[] a->cup;
|
||||
delete[] a->trigsig;
|
||||
}
|
||||
|
||||
AMSQ* AMSQ::create_amsq (int run, int size, double* in, double* out, double* 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 *a = new AMSQ;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = (double)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;
|
||||
}
|
||||
|
||||
void AMSQ::destroy_amsq (AMSQ *a)
|
||||
{
|
||||
decalc_amsq (a);
|
||||
delete a;
|
||||
}
|
||||
|
||||
void AMSQ::flush_amsq (AMSQ*a)
|
||||
{
|
||||
memset (a->trigsig, 0, a->size * sizeof (dcomplex));
|
||||
a->avsig = 0.0;
|
||||
a->state = 0;
|
||||
}
|
||||
|
||||
enum _amsqstate
|
||||
{
|
||||
MUTED,
|
||||
INCREASE,
|
||||
UNMUTED,
|
||||
TAIL,
|
||||
DECREASE
|
||||
};
|
||||
|
||||
void AMSQ::xamsq (AMSQ *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double sig, siglimit;
|
||||
for (i = 0; i < a->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;
|
||||
switch (a->state)
|
||||
{
|
||||
case MUTED:
|
||||
if (a->avsig > a->unmute_thresh)
|
||||
{
|
||||
a->state = INCREASE;
|
||||
a->count = a->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];
|
||||
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];
|
||||
if (a->count-- == 0)
|
||||
a->state = UNMUTED;
|
||||
break;
|
||||
case UNMUTED:
|
||||
if (a->avsig < a->tail_thresh)
|
||||
{
|
||||
a->state = TAIL;
|
||||
if ((siglimit = a->avsig) > 1.0) siglimit = 1.0;
|
||||
a->count = (int)((a->min_tail + (a->max_tail - a->min_tail) * (1.0 - siglimit)) * a->rate);
|
||||
}
|
||||
a->out[2 * i + 0] = a->in[2 * i + 0];
|
||||
a->out[2 * i + 1] = a->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];
|
||||
if (a->avsig > a->unmute_thresh)
|
||||
a->state = UNMUTED;
|
||||
else if (a->count-- == 0)
|
||||
{
|
||||
a->state = DECREASE;
|
||||
a->count = a->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];
|
||||
if (a->count-- == 0)
|
||||
a->state = MUTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void AMSQ::xamsqcap (AMSQ *a)
|
||||
{
|
||||
memcpy (a->trigsig, a->trigger, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void AMSQ::setBuffers_amsq (AMSQ *a, double* in, double* out, double* trigger)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->trigger = trigger;
|
||||
}
|
||||
|
||||
void AMSQ::setSamplerate_amsq (AMSQ *a, int rate)
|
||||
{
|
||||
decalc_amsq (a);
|
||||
a->rate = rate;
|
||||
calc_amsq (a);
|
||||
}
|
||||
|
||||
void AMSQ::setSize_amsq (AMSQ *a, int size)
|
||||
{
|
||||
decalc_amsq (a);
|
||||
a->size = size;
|
||||
calc_amsq (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void AMSQ::SetAMSQRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.amsq.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void AMSQ::SetAMSQThreshold (RXA& rxa, double threshold)
|
||||
{
|
||||
double thresh = pow (10.0, threshold / 20.0);
|
||||
rxa.csDSP.lock();
|
||||
rxa.amsq.p->tail_thresh = 0.9 * thresh;
|
||||
rxa.amsq.p->unmute_thresh = thresh;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void AMSQ::SetAMSQMaxTail (RXA& rxa, double tail)
|
||||
{
|
||||
AMSQ *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.amsq.p;
|
||||
if (tail < a->min_tail) tail = a->min_tail;
|
||||
a->max_tail = tail;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void AMSQ::SetAMSQRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.amsq.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void AMSQ::SetAMSQMutedGain (TXA& txa, double dBlevel)
|
||||
{ // dBlevel is negative
|
||||
AMSQ *a;
|
||||
txa.csDSP.lock();
|
||||
a = txa.amsq.p;
|
||||
a->muted_gain = pow (10.0, dBlevel / 20.0);
|
||||
compute_slews(a);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void AMSQ::SetAMSQThreshold (TXA& txa, double threshold)
|
||||
{
|
||||
double thresh = pow (10.0, threshold / 20.0);
|
||||
txa.csDSP.lock();
|
||||
txa.amsq.p->tail_thresh = 0.9 * thresh;
|
||||
txa.amsq.p->unmute_thresh = thresh;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
90
wdsp/amsq.hpp
Normal file
90
wdsp/amsq.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
/* amsq.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
#ifndef _amsq_h
|
||||
#define _amsq_h
|
||||
|
||||
#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
|
||||
double* in; // squelch input signal buffer
|
||||
double* out; // squelch output signal buffer
|
||||
double* trigger; // pointer to trigger data source
|
||||
double* 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
|
||||
int count;
|
||||
double tup;
|
||||
double tdown;
|
||||
int ntup;
|
||||
int ntdown;
|
||||
double* cup;
|
||||
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, double* in, double* out, double* 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, double* in, double* out, double* trigger);
|
||||
static void setSamplerate_amsq (AMSQ *a, int rate);
|
||||
static void setSize_amsq (AMSQ *a, 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);
|
||||
|
||||
private:
|
||||
static void compute_slews(AMSQ *a);
|
||||
static void calc_amsq(AMSQ *a);
|
||||
static void decalc_amsq (AMSQ *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
243
wdsp/anf.cpp
Normal file
243
wdsp/anf.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
/* anf.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 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 "amd.hpp"
|
||||
#include "snb.hpp"
|
||||
#include "emnr.hpp"
|
||||
#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,
|
||||
double *in_buff,
|
||||
double *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 *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;
|
||||
}
|
||||
|
||||
void ANF::destroy_anf (ANF *a)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
for (i = 0; i < a->buff_size; i++)
|
||||
{
|
||||
a->d[a->in_idx] = a->in_buff[2 * i + 0];
|
||||
|
||||
y = 0;
|
||||
sigma = 0;
|
||||
|
||||
for (j = 0; j < a->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];
|
||||
}
|
||||
inv_sigp = 1.0 / (sigma + 1e-10);
|
||||
error = a->d[a->in_idx] - y;
|
||||
|
||||
a->out_buff[2 * i + 0] = error;
|
||||
a->out_buff[2 * i + 1] = 0.0;
|
||||
|
||||
if((nel = error * (1.0 - a->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) nev = -nev;
|
||||
if (nev < nel)
|
||||
{
|
||||
if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->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;
|
||||
|
||||
for (j = 0; j < a->n_taps; j++)
|
||||
{
|
||||
idx = (a->in_idx + j + a->delay) & a->mask;
|
||||
a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
|
||||
}
|
||||
a->in_idx = (a->in_idx + a->mask) & a->mask;
|
||||
}
|
||||
}
|
||||
else if (a->in_buff != a->out_buff)
|
||||
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void ANF::flush_anf (ANF *a)
|
||||
{
|
||||
memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE);
|
||||
memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE);
|
||||
a->in_idx = 0;
|
||||
}
|
||||
|
||||
void ANF::setBuffers_anf (ANF *a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void ANF::setSamplerate_anf (ANF *a, int)
|
||||
{
|
||||
flush_anf (a);
|
||||
}
|
||||
|
||||
void ANF::setSize_anf (ANF *a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
flush_anf (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void ANF::SetANFRun (RXA& rxa, int run)
|
||||
{
|
||||
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);
|
||||
rxa.csDSP.lock();
|
||||
a->run = run;
|
||||
RXA::bp1Set (rxa);
|
||||
flush_anf (a);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ANF::SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
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);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANF::SetANFTaps (RXA& rxa, int taps)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anf.p->n_taps = taps;
|
||||
flush_anf (rxa.anf.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANF::SetANFDelay (RXA& rxa, int delay)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anf.p->delay = delay;
|
||||
flush_anf (rxa.anf.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANF::SetANFGain (RXA& rxa, double gain)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anf.p->two_mu = gain;
|
||||
flush_anf (rxa.anf.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANF::SetANFLeakage (RXA& rxa, double leakage)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anf.p->gamma = leakage;
|
||||
flush_anf (rxa.anf.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANF::SetANFPosition (RXA& rxa, int position)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anf.p->position = position;
|
||||
rxa.bp1.p->position = position;
|
||||
flush_anf (rxa.anf.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
101
wdsp/anf.hpp
Normal file
101
wdsp/anf.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
/* anf.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_anf_h
|
||||
#define wdsp_anf_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#define ANF_DLINE_SIZE 2048
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API ANF
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int buff_size;
|
||||
double *in_buff;
|
||||
double *out_buff;
|
||||
int dline_size;
|
||||
int mask;
|
||||
int n_taps;
|
||||
int delay;
|
||||
double two_mu;
|
||||
double gamma;
|
||||
double d [ANF_DLINE_SIZE];
|
||||
double w [ANF_DLINE_SIZE];
|
||||
int in_idx;
|
||||
double lidx;
|
||||
double lidx_min;
|
||||
double lidx_max;
|
||||
double ngamma;
|
||||
double den_mult;
|
||||
double lincr;
|
||||
double ldecr;
|
||||
|
||||
static ANF* create_anf(
|
||||
int run,
|
||||
int position,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *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
|
||||
);
|
||||
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, double* in, double* 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);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
245
wdsp/anr.cpp
Normal file
245
wdsp/anr.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
/* anr.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 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 <QRecursiveMutex>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "anr.hpp"
|
||||
#include "amd.hpp"
|
||||
#include "snb.hpp"
|
||||
#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,
|
||||
double *in_buff,
|
||||
double *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 *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;
|
||||
}
|
||||
|
||||
void ANR::destroy_anr (ANR *a)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
for (i = 0; i < a->buff_size; i++)
|
||||
{
|
||||
a->d[a->in_idx] = a->in_buff[2 * i + 0];
|
||||
|
||||
y = 0;
|
||||
sigma = 0;
|
||||
|
||||
for (j = 0; j < a->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];
|
||||
}
|
||||
inv_sigp = 1.0 / (sigma + 1e-10);
|
||||
error = a->d[a->in_idx] - y;
|
||||
|
||||
a->out_buff[2 * i + 0] = y;
|
||||
a->out_buff[2 * i + 1] = 0.0;
|
||||
|
||||
if((nel = error * (1.0 - a->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) nev = -nev;
|
||||
if (nev < nel)
|
||||
{
|
||||
if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->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;
|
||||
|
||||
for (j = 0; j < a->n_taps; j++)
|
||||
{
|
||||
idx = (a->in_idx + j + a->delay) & a->mask;
|
||||
a->w[j] = c0 * a->w[j] + c1 * a->d[idx];
|
||||
}
|
||||
a->in_idx = (a->in_idx + a->mask) & a->mask;
|
||||
}
|
||||
}
|
||||
else if (a->in_buff != a->out_buff)
|
||||
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void ANR::flush_anr (ANR *a)
|
||||
{
|
||||
memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE);
|
||||
memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE);
|
||||
a->in_idx = 0;
|
||||
}
|
||||
|
||||
void ANR::setBuffers_anr (ANR *a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void ANR::setSamplerate_anr (ANR *a, int)
|
||||
{
|
||||
flush_anr(a);
|
||||
}
|
||||
|
||||
void ANR::setSize_anr (ANR *a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
flush_anr(a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void ANR::SetANRRun (RXA& rxa, int run)
|
||||
{
|
||||
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);
|
||||
rxa.csDSP.lock();
|
||||
a->run = run;
|
||||
RXA::bp1Set (rxa);
|
||||
flush_anr (a);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ANR::SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
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);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANR::SetANRTaps (RXA& rxa, int taps)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anr.p->n_taps = taps;
|
||||
flush_anr (rxa.anr.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANR::SetANRDelay (RXA& rxa, int delay)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anr.p->delay = delay;
|
||||
flush_anr (rxa.anr.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANR::SetANRGain (RXA& rxa, double gain)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anr.p->two_mu = gain;
|
||||
flush_anr (rxa.anr.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANR::SetANRLeakage (RXA& rxa, double leakage)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anr.p->gamma = leakage;
|
||||
flush_anr (rxa.anr.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void ANR::SetANRPosition (RXA& rxa, int position)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.anr.p->position = position;
|
||||
rxa.bp1.p->position = position;
|
||||
flush_anr (rxa.anr.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
104
wdsp/anr.hpp
Normal file
104
wdsp/anr.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
/* anr.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_anr_h
|
||||
#define wdsp_anr_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#define ANR_DLINE_SIZE 2048
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API ANR
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int buff_size;
|
||||
double *in_buff;
|
||||
double *out_buff;
|
||||
int dline_size;
|
||||
int mask;
|
||||
int n_taps;
|
||||
int delay;
|
||||
double two_mu;
|
||||
double gamma;
|
||||
double d [ANR_DLINE_SIZE];
|
||||
double w [ANR_DLINE_SIZE];
|
||||
int in_idx;
|
||||
|
||||
double lidx;
|
||||
double lidx_min;
|
||||
double lidx_max;
|
||||
double ngamma;
|
||||
double den_mult;
|
||||
double lincr;
|
||||
double ldecr;
|
||||
|
||||
static ANR* create_anr (
|
||||
int run,
|
||||
int position,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *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
|
||||
);
|
||||
|
||||
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, double* in, double* out);
|
||||
static void setSamplerate_anr (ANR *a, int rate);
|
||||
static void setSize_anr (ANR *a, 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);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
285
wdsp/bandpass.cpp
Normal file
285
wdsp/bandpass.cpp
Normal file
@ -0,0 +1,285 @@
|
||||
/* bandpass.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 "bandpass.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
BANDPASS* BANDPASS::create_bandpass (int run, int position, int size, int nc, int mp, double* in, double* out,
|
||||
double f_low, double f_high, int samplerate, int wintype, double gain)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
BANDPASS *a = new BANDPASS;
|
||||
double* impulse;
|
||||
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;
|
||||
impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||
delete[] impulse;
|
||||
return a;
|
||||
}
|
||||
|
||||
void BANDPASS::destroy_bandpass (BANDPASS *a)
|
||||
{
|
||||
FIRCORE::destroy_fircore (a->p);
|
||||
delete a;
|
||||
}
|
||||
|
||||
void BANDPASS::flush_bandpass (BANDPASS *a)
|
||||
{
|
||||
FIRCORE::flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void BANDPASS::xbandpass (BANDPASS *a, int pos)
|
||||
{
|
||||
if (a->run && a->position == pos)
|
||||
FIRCORE::xfircore (a->p);
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void BANDPASS::setBuffers_bandpass (BANDPASS *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void BANDPASS::setSamplerate_bandpass (BANDPASS *a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
a->samplerate = rate;
|
||||
impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
|
||||
delete[] impulse;
|
||||
}
|
||||
|
||||
void BANDPASS::setSize_bandpass (BANDPASS *a, int size)
|
||||
{
|
||||
// NOTE: 'size' must be <= 'nc'
|
||||
double* impulse;
|
||||
a->size = size;
|
||||
FIRCORE::setSize_fircore (a->p, a->size);
|
||||
// recalc impulse because scale factor is a function of size
|
||||
impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
|
||||
delete[] (impulse);
|
||||
}
|
||||
|
||||
void BANDPASS::setGain_bandpass (BANDPASS *a, double gain, int update)
|
||||
{
|
||||
double* impulse;
|
||||
a->gain = gain;
|
||||
impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, update);
|
||||
delete[] (impulse);
|
||||
}
|
||||
|
||||
void BANDPASS::CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, double gain)
|
||||
{
|
||||
double* impulse;
|
||||
if ((a->f_low != f_low) || (a->f_high != f_high) || (a->gain != gain))
|
||||
{
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
a->gain = gain;
|
||||
impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
|
||||
delete[] (impulse);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void BANDPASS::SetBandpassFreqs (RXA& rxa, double f_low, double f_high)
|
||||
{
|
||||
double* impulse;
|
||||
BANDPASS *a = rxa.bp1.p;
|
||||
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||
{
|
||||
impulse = FIR::fir_bandpass (a->nc, f_low, f_high, a->samplerate,
|
||||
a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 0);
|
||||
delete[] (impulse);
|
||||
rxa.csDSP.lock();
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
FIRCORE::setUpdate_fircore (a->p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void BANDPASS::SetBandpassNC (RXA& rxa, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
double* impulse;
|
||||
BANDPASS *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.bp1.p;
|
||||
if (nc != a->nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
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);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void BANDPASS::SetBandpassMP (RXA& rxa, int mp)
|
||||
{
|
||||
BANDPASS *a;
|
||||
a = rxa.bp1.p;
|
||||
if (mp != a->mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
FIRCORE::setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
//PORT
|
||||
//void SetTXABandpassFreqs (int channel, double f_low, double f_high)
|
||||
//{
|
||||
// double* 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 / (double)(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 / (double)(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 / (double)(2 * a->size));
|
||||
// setImpulse_fircore (a->p, impulse, 1);
|
||||
// delete[] (impulse);
|
||||
// }
|
||||
//}
|
||||
|
||||
void BANDPASS::SetBandpassNC (TXA& txa, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
double* impulse;
|
||||
BANDPASS *a;
|
||||
txa.csDSP.lock();
|
||||
a = txa.bp0.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
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
|
||||
90
wdsp/bandpass.hpp
Normal file
90
wdsp/bandpass.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
/* bandpass.h
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_bandpass_h
|
||||
#define wdsp_bandpass_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class RXA;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API BANDPASS
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int size;
|
||||
int nc;
|
||||
int mp;
|
||||
double* in;
|
||||
double* out;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double samplerate;
|
||||
int wintype;
|
||||
double gain;
|
||||
FIRCORE *p;
|
||||
|
||||
static BANDPASS *create_bandpass (int run, int position, int size, int nc, int mp, double* in, double* out,
|
||||
double f_low, double f_high, int samplerate, 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, double* in, double* 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);
|
||||
// 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);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
404
wdsp/bldr.cpp
Normal file
404
wdsp/bldr.cpp
Normal file
@ -0,0 +1,404 @@
|
||||
/* lmath.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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 "bldr.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
BLDR* BLDR::create_builder(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 double[2 * points]; // (double*)malloc0(2 * points * sizeof(double));
|
||||
a->sx = new double[points]; // (double*)malloc0( points * sizeof(double));
|
||||
a->sy = new double[points]; // (double*)malloc0( points * sizeof(double));
|
||||
a->h = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->p = new int[ints]; // (int*) malloc0( ints * sizeof(int));
|
||||
a->np = new int[ints]; // (int*) malloc0( ints * sizeof(int));
|
||||
a->taa = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tab = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tag = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tad = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tbb = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tbg = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tbd = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tgg = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tgd = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
a->tdd = new double[ints]; // (double*)malloc0( ints * sizeof(double));
|
||||
int nsize = 3 * ints + 1;
|
||||
int intp1 = ints + 1;
|
||||
int intm1 = ints - 1;
|
||||
a->A = new double[intp1 * intp1]; // (double*)malloc0(intp1 * intp1 * sizeof(double));
|
||||
a->B = new double[intp1 * intp1]; // (double*)malloc0(intp1 * intp1 * sizeof(double));
|
||||
a->C = new double[intp1 * intp1]; // (double*)malloc0(intm1 * intp1 * sizeof(double));
|
||||
a->D = new double[intp1]; // (double*)malloc0(intp1 * sizeof(double));
|
||||
a->E = new double[intp1 * intp1]; // (double*)malloc0(intp1 * intp1 * sizeof(double));
|
||||
a->F = new double[intm1 * intp1]; // (double*)malloc0(intm1 * intp1 * sizeof(double));
|
||||
a->G = new double[intp1]; // (double*)malloc0(intp1 * sizeof(double));
|
||||
a->MAT = new double[nsize * nsize]; // (double*)malloc0(nsize * nsize * sizeof(double));
|
||||
a->RHS = new double[nsize]; // (double*)malloc0(nsize * sizeof(double));
|
||||
a->SLN = new double[nsize]; // (double*)malloc0(nsize * sizeof(double));
|
||||
a->z = new double[intp1]; // (double*)malloc0(intp1 * sizeof(double));
|
||||
a->zp = new double[intp1]; // (double*)malloc0(intp1 * sizeof(double));
|
||||
a->wrk = new double[nsize]; // (double*)malloc0(nsize * sizeof(double));
|
||||
a->ipiv = new int[nsize]; // (int*) malloc0(nsize * sizeof(int));
|
||||
return a;
|
||||
}
|
||||
|
||||
void BLDR::destroy_builder(BLDR *a)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void BLDRflush_builder(BLDR *a, int points, int ints)
|
||||
{
|
||||
memset(a->catxy, 0, 2 * points * sizeof(double));
|
||||
memset(a->sx, 0, points * sizeof(double));
|
||||
memset(a->sy, 0, points * sizeof(double));
|
||||
memset(a->h, 0, ints * sizeof(double));
|
||||
memset(a->p, 0, ints * sizeof(int));
|
||||
memset(a->np, 0, ints * sizeof(int));
|
||||
memset(a->taa, 0, ints * sizeof(double));
|
||||
memset(a->tab, 0, ints * sizeof(double));
|
||||
memset(a->tag, 0, ints * sizeof(double));
|
||||
memset(a->tad, 0, ints * sizeof(double));
|
||||
memset(a->tbb, 0, ints * sizeof(double));
|
||||
memset(a->tbg, 0, ints * sizeof(double));
|
||||
memset(a->tbd, 0, ints * sizeof(double));
|
||||
memset(a->tgg, 0, ints * sizeof(double));
|
||||
memset(a->tgd, 0, ints * sizeof(double));
|
||||
memset(a->tdd, 0, ints * sizeof(double));
|
||||
int nsize = 3 * ints + 1;
|
||||
int intp1 = ints + 1;
|
||||
int intm1 = ints - 1;
|
||||
memset(a->A, 0, intp1 * intp1 * sizeof(double));
|
||||
memset(a->B, 0, intp1 * intp1 * sizeof(double));
|
||||
memset(a->C, 0, intm1 * intp1 * sizeof(double));
|
||||
memset(a->D, 0, intp1 * sizeof(double));
|
||||
memset(a->E, 0, intp1 * intp1 * sizeof(double));
|
||||
memset(a->F, 0, intm1 * intp1 * sizeof(double));
|
||||
memset(a->G, 0, intp1 * sizeof(double));
|
||||
memset(a->MAT, 0, nsize * nsize * sizeof(double));
|
||||
memset(a->RHS, 0, nsize * sizeof(double));
|
||||
memset(a->SLN, 0, nsize * sizeof(double));
|
||||
memset(a->z, 0, intp1 * sizeof(double));
|
||||
memset(a->zp, 0, intp1 * sizeof(double));
|
||||
memset(a->wrk, 0, nsize * sizeof(double));
|
||||
memset(a->ipiv, 0, nsize * sizeof(int));
|
||||
}
|
||||
|
||||
int BLDR::fcompare(const void* a, const void* b)
|
||||
{
|
||||
if (*(double*)a < *(double*)b)
|
||||
return -1;
|
||||
else if (*(double*)a == *(double*)b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void BLDR::decomp(int n, double* a, int* piv, int* info, double* wrk)
|
||||
{
|
||||
int i, j, k;
|
||||
int t_piv;
|
||||
double m_row, mt_row, m_col, mt_col;
|
||||
*info = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
piv[i] = i;
|
||||
m_row = 0.0;
|
||||
for (j = 0; j < n; j++)
|
||||
{
|
||||
mt_row = a[n * i + j];
|
||||
if (mt_row < 0.0) mt_row = -mt_row;
|
||||
if (mt_row > m_row) m_row = mt_row;
|
||||
}
|
||||
if (m_row == 0.0)
|
||||
{
|
||||
*info = i;
|
||||
goto cleanup;
|
||||
}
|
||||
wrk[i] = m_row;
|
||||
}
|
||||
for (k = 0; k < n - 1; k++)
|
||||
{
|
||||
j = k;
|
||||
m_col = a[n * piv[k] + k] / wrk[piv[k]];
|
||||
if (m_col < 0) m_col = -m_col;
|
||||
for (i = k + 1; i < n; i++)
|
||||
{
|
||||
mt_col = a[n * piv[i] + k] / wrk[piv[k]];
|
||||
if (mt_col < 0.0) mt_col = -mt_col;
|
||||
if (mt_col > m_col)
|
||||
{
|
||||
m_col = mt_col;
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
if (m_col == 0)
|
||||
{
|
||||
*info = -k;
|
||||
goto cleanup;
|
||||
}
|
||||
t_piv = piv[k];
|
||||
piv[k] = piv[j];
|
||||
piv[j] = t_piv;
|
||||
for (i = k + 1; i < n; i++)
|
||||
{
|
||||
a[n * piv[i] + k] /= a[n * piv[k] + k];
|
||||
for (j = k + 1; j < n; j++)
|
||||
a[n * piv[i] + j] -= a[n * piv[i] + k] * a[n * piv[k] + j];
|
||||
}
|
||||
}
|
||||
if (a[n * n - 1] == 0.0)
|
||||
*info = -n;
|
||||
cleanup:
|
||||
return;
|
||||
}
|
||||
|
||||
void BLDR::dsolve(int n, double* a, int* piv, double* b, double* x)
|
||||
{
|
||||
int j, k;
|
||||
double sum;
|
||||
|
||||
for (k = 0; k < n; k++)
|
||||
{
|
||||
sum = 0.0;
|
||||
for (j = 0; j < k; j++)
|
||||
sum += a[n * piv[k] + j] * x[j];
|
||||
x[k] = b[piv[k]] - sum;
|
||||
}
|
||||
|
||||
for (k = n - 1; k >= 0; k--)
|
||||
{
|
||||
sum = 0.0;
|
||||
for (j = k + 1; j < n; j++)
|
||||
sum += a[n * piv[k] + j] * x[j];
|
||||
x[k] = (x[k] - sum) / a[n * piv[k] + k];
|
||||
}
|
||||
}
|
||||
|
||||
void BLDR::cull(int* n, int ints, double* x, double* t, double ptol)
|
||||
{
|
||||
int k = 0;
|
||||
int i = *n;
|
||||
int ntopint;
|
||||
int npx;
|
||||
|
||||
while (x[i - 1] > t[ints - 1])
|
||||
i--;
|
||||
ntopint = *n - i;
|
||||
npx = (int)(ntopint * (1.0 - ptol));
|
||||
i = *n;
|
||||
while ((k < npx) && (x[--i] > t[ints]))
|
||||
k++;
|
||||
*n -= k;
|
||||
}
|
||||
|
||||
void BLDR::xbuilder(BLDR *a, int points, double* x, double* y, int ints, double* t, int* info, double* c, double ptol)
|
||||
{
|
||||
double u, v, alpha, beta, gamma, delta;
|
||||
int nsize = 3 * ints + 1;
|
||||
int intp1 = ints + 1;
|
||||
int intm1 = ints - 1;
|
||||
int i, j, k, m;
|
||||
int dinfo;
|
||||
flush_builder(a, points, ints);
|
||||
for (i = 0; i < points; i++)
|
||||
{
|
||||
a->catxy[2 * i + 0] = x[i];
|
||||
a->catxy[2 * i + 1] = y[i];
|
||||
}
|
||||
qsort(a->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];
|
||||
}
|
||||
cull(&points, ints, a->sx, t, ptol);
|
||||
if (points <= 0 || a->sx[points - 1] > t[ints])
|
||||
{
|
||||
*info = -1000;
|
||||
goto cleanup;
|
||||
}
|
||||
else *info = 0;
|
||||
|
||||
for (j = 0; j < ints; j++)
|
||||
a->h[j] = t[j + 1] - t[j];
|
||||
a->p[0] = 0;
|
||||
j = 0;
|
||||
for (i = 0; i < points; i++)
|
||||
{
|
||||
if (a->sx[i] <= t[j + 1])
|
||||
a->np[j]++;
|
||||
else
|
||||
{
|
||||
a->p[++j] = i;
|
||||
while (a->sx[i] > t[j + 1])
|
||||
a->p[++j] = i;
|
||||
a->np[j] = 1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ints; i++)
|
||||
for (j = a->p[i]; j < a->p[i] + a->np[i]; j++)
|
||||
{
|
||||
u = (a->sx[j] - t[i]) / a->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;
|
||||
}
|
||||
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];
|
||||
}
|
||||
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];
|
||||
}
|
||||
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];
|
||||
for (j = 0, m = intp1; j < intp1; j++, m++)
|
||||
a->MAT[k * nsize + m] = a->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];
|
||||
}
|
||||
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];
|
||||
for (j = 0, m = intp1; j < intp1; j++, m++)
|
||||
a->MAT[k * nsize + m] = a->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];
|
||||
}
|
||||
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];
|
||||
for (j = 0, m = intp1; j < intp1; j++, m++)
|
||||
a->MAT[k * nsize + m] = a->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;
|
||||
}
|
||||
decomp(nsize, a->MAT, a->ipiv, &dinfo, a->wrk);
|
||||
dsolve(nsize, a->MAT, a->ipiv, a->RHS, a->SLN);
|
||||
if (dinfo != 0)
|
||||
{
|
||||
*info = dinfo;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i <= ints; i++)
|
||||
{
|
||||
a->z[i] = a->SLN[i];
|
||||
a->zp[i] = a->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]);
|
||||
}
|
||||
cleanup:
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
83
wdsp/bldr.hpp
Normal file
83
wdsp/bldr.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
/* lmath.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_bldr_h
|
||||
#define wdsp_bldr_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API BLDR
|
||||
{
|
||||
public:
|
||||
double* catxy;
|
||||
double* sx;
|
||||
double* sy;
|
||||
double* h;
|
||||
int* p;
|
||||
int* np;
|
||||
double* taa;
|
||||
double* tab;
|
||||
double* tag;
|
||||
double* tad;
|
||||
double* tbb;
|
||||
double* tbg;
|
||||
double* tbd;
|
||||
double* tgg;
|
||||
double* tgd;
|
||||
double* tdd;
|
||||
double* A;
|
||||
double* B;
|
||||
double* C;
|
||||
double* D;
|
||||
double* E;
|
||||
double* F;
|
||||
double* G;
|
||||
double* MAT;
|
||||
double* RHS;
|
||||
double* SLN;
|
||||
double* z;
|
||||
double* zp;
|
||||
double* wrk;
|
||||
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, double* x, double* y, int ints, double* t, int* info, double* c, double ptol);
|
||||
|
||||
private:
|
||||
static int fcompare(const void* a, const void* b);
|
||||
static void decomp(int n, double* a, int* piv, int* info, double* wrk);
|
||||
static void dsolve(int n, double* a, int* piv, double* b, double* x);
|
||||
static void cull(int* n, int ints, double* x, double* t, double ptol);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
281
wdsp/bps.cpp
Normal file
281
wdsp/bps.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
/* bandpass.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 <QRecursiveMutex>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "bps.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "bandpass.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void BPS::calc_bps (BPS *a)
|
||||
{
|
||||
double* impulse;
|
||||
a->infilt = new double[2 * a->size * 2]; // (double *)malloc0(2 * a->size * sizeof(dcomplex));
|
||||
a->product = new double[2 * a->size * 2]; // (double *)malloc0(2 * a->size * sizeof(dcomplex));
|
||||
impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = FIR::fftcv_mults(2 * a->size, impulse);
|
||||
a->CFor = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->infilt, (fftw_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->CRev = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->product, (fftw_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
delete[](impulse);
|
||||
}
|
||||
|
||||
void BPS::decalc_bps (BPS *a)
|
||||
{
|
||||
fftw_destroy_plan(a->CRev);
|
||||
fftw_destroy_plan(a->CFor);
|
||||
delete[] (a->mults);
|
||||
delete[] (a->product);
|
||||
delete[] (a->infilt);
|
||||
}
|
||||
|
||||
BPS* BPS::create_bps (int run, int position, int size, double* in, double* out,
|
||||
double f_low, double f_high, int samplerate, int wintype, double gain)
|
||||
{
|
||||
BPS *a = new BPS;
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->samplerate = (double)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;
|
||||
}
|
||||
|
||||
void BPS::destroy_bps (BPS *a)
|
||||
{
|
||||
decalc_bps (a);
|
||||
delete a;
|
||||
}
|
||||
|
||||
void BPS::flush_bps (BPS *a)
|
||||
{
|
||||
memset (a->infilt, 0, 2 * a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void BPS::xbps (BPS *a, int pos)
|
||||
{
|
||||
int i;
|
||||
double I, Q;
|
||||
if (a->run && pos == a->position)
|
||||
{
|
||||
memcpy (&(a->infilt[2 * a->size]), a->in, a->size * sizeof (dcomplex));
|
||||
fftw_execute (a->CFor);
|
||||
for (i = 0; i < 2 * a->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];
|
||||
}
|
||||
fftw_execute (a->CRev);
|
||||
memcpy (a->infilt, &(a->infilt[2 * a->size]), a->size * sizeof(dcomplex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void BPS::setBuffers_bps (BPS *a, double* in, double* out)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
void BPS::setSamplerate_bps (BPS *a, int rate)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->samplerate = rate;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
void BPS::setSize_bps (BPS *a, int size)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->size = size;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
void BPS::setFreqs_bps (BPS *a, double f_low, double f_high)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Bandpass: RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void BPS::SetBPSRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.bp1.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void BPS::SetBPSFreqs (RXA& rxa, double f_low, double f_high)
|
||||
{
|
||||
double* impulse;
|
||||
BPS *a1;
|
||||
rxa.csDSP.lock();
|
||||
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 / (double)(2 * a1->size));
|
||||
a1->mults = FIR::fftcv_mults (2 * a1->size, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void BPS::SetBPSWindow (RXA& rxa, int wintype)
|
||||
{
|
||||
double* impulse;
|
||||
BPS *a1;
|
||||
rxa.csDSP.lock();
|
||||
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 / (double)(2 * a1->size));
|
||||
a1->mults = FIR::fftcv_mults (2 * a1->size, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
// UNCOMMENT properties when pointers in place in txa
|
||||
void BPS::SetBPSRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.bp1.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void BPS::SetBPSFreqs (TXA& txa, double f_low, double f_high)
|
||||
{
|
||||
double* impulse;
|
||||
BPS *a;
|
||||
txa.csDSP.lock();
|
||||
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 / (double)(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 / (double)(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 / (double)(2 * a->size));
|
||||
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void BPS::SetBPSWindow (TXA& txa, int wintype)
|
||||
{
|
||||
double* impulse;
|
||||
BPS *a;
|
||||
txa.csDSP.lock();
|
||||
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 / (double)(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 / (double)(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 / (double)(2 * a->size));
|
||||
a->mults = FIR::fftcv_mults (2 * a->size, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
91
wdsp/bps.hpp
Normal file
91
wdsp/bps.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
/* bandpass.h
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_bps_h
|
||||
#define wdsp_bps_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API BPS
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double* infilt;
|
||||
double* product;
|
||||
double* mults;
|
||||
double samplerate;
|
||||
int wintype;
|
||||
double gain;
|
||||
fftw_plan CFor;
|
||||
fftw_plan CRev;
|
||||
|
||||
static BPS* create_bps (int run, int position, int size, double* in, double* out,
|
||||
double f_low, double f_high, int samplerate, int wintype, double 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, double* in, double* out);
|
||||
static void setSamplerate_bps (BPS *a, int rate);
|
||||
static void setSize_bps (BPS *a, int size);
|
||||
static void setFreqs_bps (BPS *a, double f_low, double f_high);
|
||||
// RXA Prototypes
|
||||
static void SetBPSRun (RXA& rxa, int run);
|
||||
static void SetBPSFreqs (RXA& rxa, double low, double high);
|
||||
static void SetBPSWindow (RXA& rxa, int wintype);
|
||||
// TXA Prototypes
|
||||
static void SetBPSRun (TXA& txa, int run);
|
||||
static void SetBPSFreqs (TXA& txa, double low, double high);
|
||||
static void SetBPSWindow (TXA& txa, int wintype);
|
||||
|
||||
private:
|
||||
static void calc_bps (BPS *a);
|
||||
static void decalc_bps (BPS *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
29078
wdsp/calculus.cpp
Normal file
29078
wdsp/calculus.cpp
Normal file
File diff suppressed because it is too large
Load Diff
45
wdsp/calculus.hpp
Normal file
45
wdsp/calculus.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
/* calculus.hpp
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_calculus_h
|
||||
#define wdsp_calculus_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API Calculus
|
||||
{
|
||||
public:
|
||||
static const double GG[];
|
||||
static const double GGS[];
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
132
wdsp/cblock.cpp
Normal file
132
wdsp/cblock.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/* cblock.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "cblock.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void CBL::calc_cbl (CBL *a)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
CBL* CBL::create_cbl
|
||||
(
|
||||
int run,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *out_buff,
|
||||
int mode,
|
||||
int sample_rate,
|
||||
double 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;
|
||||
}
|
||||
|
||||
void CBL::destroy_cbl(CBL *a)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
void CBL::flush_cbl (CBL *a)
|
||||
{
|
||||
a->prevIin = 0.0;
|
||||
a->prevQin = 0.0;
|
||||
a->prevIout = 0.0;
|
||||
a->prevQout = 0.0;
|
||||
}
|
||||
|
||||
void CBL::xcbl (CBL *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double tempI, tempQ;
|
||||
for (i = 0; i < a->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;
|
||||
if (fabs(a->prevIout = a->out_buff[2 * i + 0]) < 1.0e-100) a->prevIout = 0.0;
|
||||
if (fabs(a->prevQout = a->out_buff[2 * i + 1]) < 1.0e-100) a->prevQout = 0.0;
|
||||
}
|
||||
}
|
||||
else if (a->in_buff != a->out_buff)
|
||||
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void CBL::setBuffers_cbl (CBL *a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void CBL::setSamplerate_cbl (CBL *a, int rate)
|
||||
{
|
||||
a->sample_rate = rate;
|
||||
calc_cbl (a);
|
||||
}
|
||||
|
||||
void CBL::setSize_cbl (CBL *a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
flush_cbl (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void CBL::SetCBLRun(RXA& rxa, int setit)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.cbl.p->run = setit;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
78
wdsp/cblock.hpp
Normal file
78
wdsp/cblock.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
/* cblock.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_cblock_h
|
||||
#define wdsp_cblock_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API CBL
|
||||
{
|
||||
public:
|
||||
int run; //run
|
||||
int buff_size; //buffer size
|
||||
double *in_buff; //pointer to input buffer
|
||||
double *out_buff; //pointer to output buffer
|
||||
int mode;
|
||||
double sample_rate; //sample rate
|
||||
double prevIin;
|
||||
double prevQin;
|
||||
double prevIout;
|
||||
double prevQout;
|
||||
double tau; //carrier removal time constant
|
||||
double mtau; //carrier removal multiplier
|
||||
|
||||
static CBL* create_cbl
|
||||
(
|
||||
int run,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *out_buff,
|
||||
int mode,
|
||||
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, double* in, double* 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);
|
||||
|
||||
private:
|
||||
static void calc_cbl (CBL *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
521
wdsp/cfcomp.cpp
Normal file
521
wdsp/cfcomp.cpp
Normal file
@ -0,0 +1,521 @@
|
||||
/* cfcomp.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2017, 2021 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 "cfcomp.hpp"
|
||||
#include "meterlog10.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void CFCOMP::calc_cfcwindow (CFCOMP *a)
|
||||
{
|
||||
int i;
|
||||
double arg0, arg1, cgsum, igsum, coherent_gain, inherent_power_gain, wmult;
|
||||
switch (a->wintype)
|
||||
{
|
||||
case 0:
|
||||
arg0 = 2.0 * PI / (double)a->fsize;
|
||||
cgsum = 0.0;
|
||||
igsum = 0.0;
|
||||
for (i = 0; i < a->fsize; i++)
|
||||
{
|
||||
a->window[i] = sqrt (0.54 - 0.46 * cos((double)i * arg0));
|
||||
cgsum += a->window[i];
|
||||
igsum += a->window[i] * a->window[i];
|
||||
}
|
||||
coherent_gain = cgsum / (double)a->fsize;
|
||||
inherent_power_gain = igsum / (double)a->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);
|
||||
break;
|
||||
case 1:
|
||||
arg0 = 2.0 * PI / (double)a->fsize;
|
||||
cgsum = 0.0;
|
||||
igsum = 0.0;
|
||||
for (i = 0; i < a->fsize; i++)
|
||||
{
|
||||
arg1 = cos(arg0 * (double)i);
|
||||
a->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];
|
||||
}
|
||||
coherent_gain = cgsum / (double)a->fsize;
|
||||
inherent_power_gain = igsum / (double)a->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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int CFCOMP::fCOMPcompare (const void *a, const void *b)
|
||||
{
|
||||
if (*(double*)a < *(double*)b)
|
||||
return -1;
|
||||
else if (*(double*)a == *(double*)b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CFCOMP::calc_comp (CFCOMP *a)
|
||||
{
|
||||
int i, j;
|
||||
double f, frac, fincr, fmax;
|
||||
double* 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++)
|
||||
{
|
||||
a->F[i] = std::max (a->F[i], 0.0);
|
||||
a->F[i] = std::min (a->F[i], fmax);
|
||||
a->G[i] = std::max (a->G[i], 0.0);
|
||||
}
|
||||
sary = new double[3 * a->nfreqs]; // (double *)malloc0 (3 * a->nfreqs * sizeof (double));
|
||||
for (i = 0; i < a->nfreqs; i++)
|
||||
{
|
||||
sary[3 * i + 0] = a->F[i];
|
||||
sary[3 * i + 1] = a->G[i];
|
||||
sary[3 * i + 2] = a->E[i];
|
||||
}
|
||||
qsort (sary, a->nfreqs, 3 * sizeof (double), fCOMPcompare);
|
||||
for (i = 0; i < a->nfreqs; i++)
|
||||
{
|
||||
a->F[i] = sary[3 * i + 0];
|
||||
a->G[i] = sary[3 * i + 1];
|
||||
a->E[i] = sary[3 * i + 2];
|
||||
}
|
||||
delete[] (sary);
|
||||
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++)
|
||||
{
|
||||
a->fp[j] = a->F[i];
|
||||
a->gp[j] = a->G[i];
|
||||
a->ep[j] = a->E[i];
|
||||
}
|
||||
fincr = a->rate / (double)a->fsize;
|
||||
j = 0;
|
||||
// print_impulse ("gp.txt", a->nfreqs+2, a->gp, 0, 0);
|
||||
for (i = 0; i < a->msize; i++)
|
||||
{
|
||||
f = fincr * (double)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];
|
||||
}
|
||||
// print_impulse ("comp.txt", a->msize, a->comp, 0, 0);
|
||||
delete[] sary;
|
||||
}
|
||||
|
||||
void CFCOMP::calc_cfcomp(CFCOMP *a)
|
||||
{
|
||||
int i;
|
||||
a->incr = a->fsize / a->ovrlp;
|
||||
if (a->fsize > a->bsize)
|
||||
a->iasize = a->fsize;
|
||||
else
|
||||
a->iasize = a->bsize + a->fsize - a->incr;
|
||||
a->iainidx = 0;
|
||||
a->iaoutidx = 0;
|
||||
if (a->fsize > a->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;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->oasize = a->bsize;
|
||||
a->oainidx = a->fsize - a->incr;
|
||||
}
|
||||
a->init_oainidx = a->oainidx;
|
||||
a->oaoutidx = 0;
|
||||
a->msize = a->fsize / 2 + 1;
|
||||
a->window = new double[a->fsize]; // (double *)malloc0 (a->fsize * sizeof(double));
|
||||
a->inaccum = new double[a->iasize]; // (double *)malloc0 (a->iasize * sizeof(double));
|
||||
a->forfftin = new double[a->fsize]; // (double *)malloc0 (a->fsize * sizeof(double));
|
||||
a->forfftout = new double[a->msize * 2]; // (double *)malloc0 (a->msize * sizeof(complex));
|
||||
a->cmask = new double[a->msize]; // (double *)malloc0 (a->msize * sizeof(double));
|
||||
a->mask = new double[a->msize]; // (double *)malloc0 (a->msize * sizeof(double));
|
||||
a->cfc_gain = new double[a->msize]; // (double *)malloc0 (a->msize * sizeof(double));
|
||||
a->revfftin = new double[a->msize * 2]; // (double *)malloc0 (a->msize * sizeof(complex));
|
||||
a->revfftout = new double[a->fsize]; // (double *)malloc0 (a->fsize * sizeof(double));
|
||||
a->save = new double*[a->ovrlp]; // (double **)malloc0(a->ovrlp * sizeof(double *));
|
||||
for (i = 0; i < a->ovrlp; i++)
|
||||
a->save[i] = new double[a->fsize]; // (double *)malloc0(a->fsize * sizeof(double));
|
||||
a->outaccum = new double[a->oasize]; // (double *)malloc0(a->oasize * sizeof(double));
|
||||
a->nsamps = 0;
|
||||
a->saveidx = 0;
|
||||
a->Rfor = fftw_plan_dft_r2c_1d(a->fsize, a->forfftin, (fftw_complex *)a->forfftout, FFTW_ESTIMATE);
|
||||
a->Rrev = fftw_plan_dft_c2r_1d(a->fsize, (fftw_complex *)a->revfftin, a->revfftout, FFTW_ESTIMATE);
|
||||
calc_cfcwindow(a);
|
||||
|
||||
a->pregain = (2.0 * a->winfudge) / (double)a->fsize;
|
||||
a->postgain = 0.5 / ((double)a->ovrlp * a->winfudge);
|
||||
|
||||
a->fp = new double[a->nfreqs + 2]; // (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->gp = new double[a->nfreqs + 2]; // (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->ep = new double[a->nfreqs + 2]; // (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->comp = new double[a->msize]; // (double *) malloc0 (a->msize * sizeof (double));
|
||||
a->peq = new double[a->msize]; // (double *) malloc0 (a->msize * sizeof (double));
|
||||
calc_comp (a);
|
||||
|
||||
a->gain = 0.0;
|
||||
a->mmult = exp (-1.0 / (a->rate * a->ovrlp * a->mtau));
|
||||
a->dmult = exp (-(double)a->fsize / (a->rate * a->ovrlp * a->dtau));
|
||||
|
||||
a->delta = new double[a->msize]; // (double*)malloc0 (a->msize * sizeof(double));
|
||||
a->delta_copy = new double[a->msize]; // (double*)malloc0 (a->msize * sizeof(double));
|
||||
a->cfc_gain_copy = new double[a->msize]; // (double*)malloc0 (a->msize * sizeof(double));
|
||||
}
|
||||
|
||||
void CFCOMP::decalc_cfcomp(CFCOMP *a)
|
||||
{
|
||||
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);
|
||||
|
||||
fftw_destroy_plan(a->Rrev);
|
||||
fftw_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, double* in, double* out, int fsize, int ovrlp,
|
||||
int rate, int wintype, int comp_method, int nfreqs, double precomp, double prepeq, double* F, double* G, double* E, double mtau, double 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 double[a->nfreqs]; // (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->G = new double[a->nfreqs]; // (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->E = new double[a->nfreqs]; // (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
memcpy (a->F, F, a->nfreqs * sizeof (double));
|
||||
memcpy (a->G, G, a->nfreqs * sizeof (double));
|
||||
memcpy (a->E, E, a->nfreqs * sizeof (double));
|
||||
calc_cfcomp (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void CFCOMP::flush_cfcomp (CFCOMP *a)
|
||||
{
|
||||
int i;
|
||||
memset (a->inaccum, 0, a->iasize * sizeof (double));
|
||||
for (i = 0; i < a->ovrlp; i++)
|
||||
memset (a->save[i], 0, a->fsize * sizeof (double));
|
||||
memset (a->outaccum, 0, a->oasize * sizeof (double));
|
||||
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(double));
|
||||
}
|
||||
|
||||
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;
|
||||
double comp, mask, delta;
|
||||
switch (a->comp_method)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
double 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;
|
||||
|
||||
delta = a->cfc_gain[i] - a->cmask[i];
|
||||
if (delta > a->delta[i]) a->delta[i] = delta;
|
||||
else a->delta[i] *= a->dmult;
|
||||
}
|
||||
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 (double));
|
||||
// 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;
|
||||
fftw_execute (a->Rfor);
|
||||
calc_mask(a);
|
||||
for (i = 0; i < a->msize; 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];
|
||||
}
|
||||
fftw_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)
|
||||
{
|
||||
if ( i == a->ovrlp)
|
||||
a->outaccum[k] = a->save[sbuff][j];
|
||||
else
|
||||
a->outaccum[k] += a->save[sbuff][j];
|
||||
}
|
||||
}
|
||||
a->saveidx = (a->saveidx + 1) % a->ovrlp;
|
||||
a->oainidx = (a->oainidx + a->incr) % a->oasize;
|
||||
}
|
||||
for (i = 0; i < a->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;
|
||||
}
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->bsize * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void CFCOMP::setBuffers_cfcomp (CFCOMP *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void CFCOMP::setSamplerate_cfcomp (CFCOMP *a, int rate)
|
||||
{
|
||||
decalc_cfcomp (a);
|
||||
a->rate = rate;
|
||||
calc_cfcomp (a);
|
||||
}
|
||||
|
||||
void CFCOMP::setSize_cfcomp (CFCOMP *a, int size)
|
||||
{
|
||||
decalc_cfcomp (a);
|
||||
a->bsize = size;
|
||||
calc_cfcomp (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void CFCOMP::SetCFCOMPRun (TXA& txa, int run)
|
||||
{
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
a->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CFCOMP::SetCFCOMPPosition (TXA& txa, int pos)
|
||||
{
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
if (a->position != pos)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
a->position = pos;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CFCOMP::SetCFCOMPprofile (TXA& txa, int nfreqs, double* F, double* G, double *E)
|
||||
{
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
txa.csDSP.lock();
|
||||
a->nfreqs = nfreqs;
|
||||
delete[] (a->E);
|
||||
delete[] (a->F);
|
||||
delete[] (a->G);
|
||||
a->F = new double[a->nfreqs]; // (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->G = new double[a->nfreqs]; // (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->E = new double[a->nfreqs]; // (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
memcpy (a->F, F, a->nfreqs * sizeof (double));
|
||||
memcpy (a->G, G, a->nfreqs * sizeof (double));
|
||||
memcpy (a->E, E, a->nfreqs * sizeof (double));
|
||||
delete[] (a->ep);
|
||||
delete[] (a->gp);
|
||||
delete[] (a->fp);
|
||||
a->fp = new double[a->nfreqs]; // (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->gp = new double[a->nfreqs]; // (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->ep = new double[a->nfreqs]; // (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
calc_comp(a);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void CFCOMP::SetCFCOMPPrecomp (TXA& txa, double precomp)
|
||||
{
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
if (a->precomp != precomp)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
a->precomp = precomp;
|
||||
a->precomplin = pow (10.0, 0.05 * a->precomp);
|
||||
for (int i = 0; i < a->msize; i++)
|
||||
{
|
||||
a->cfc_gain[i] = a->precomplin * a->comp[i];
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CFCOMP::SetCFCOMPPeqRun (TXA& txa, int run)
|
||||
{
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
if (a->peq_run != run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
a->peq_run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CFCOMP::SetCFCOMPPrePeq (TXA& txa, double prepeq)
|
||||
{
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
txa.csDSP.lock();
|
||||
a->prepeq = prepeq;
|
||||
a->prepeqlin = pow (10.0, 0.05 * a->prepeq);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void CFCOMP::GetCFCOMPDisplayCompression (TXA& txa, double* comp_values, int* ready)
|
||||
{
|
||||
int i;
|
||||
CFCOMP *a = txa.cfcomp.p;
|
||||
txa.csDSP.lock();
|
||||
if ((*ready = a->mask_ready))
|
||||
{
|
||||
memcpy(a->delta_copy, a->delta, a->msize * sizeof(double));
|
||||
memcpy(a->cfc_gain_copy, a->cfc_gain, a->msize * sizeof(double));
|
||||
a->mask_ready = 0;
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
152
wdsp/cfcomp.hpp
Normal file
152
wdsp/cfcomp.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
/* cfcomp.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2017, 2021 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_cfcomp_h
|
||||
#define wdsp_cfcomp_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API CFCOMP
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int bsize;
|
||||
double* in;
|
||||
double* out;
|
||||
int fsize;
|
||||
int ovrlp;
|
||||
int incr;
|
||||
double* window;
|
||||
int iasize;
|
||||
double* inaccum;
|
||||
double* forfftin;
|
||||
double* forfftout;
|
||||
int msize;
|
||||
double* cmask;
|
||||
double* mask;
|
||||
int mask_ready;
|
||||
double* cfc_gain;
|
||||
double* revfftin;
|
||||
double* revfftout;
|
||||
double** save;
|
||||
int oasize;
|
||||
double* outaccum;
|
||||
double rate;
|
||||
int wintype;
|
||||
double pregain;
|
||||
double postgain;
|
||||
int nsamps;
|
||||
int iainidx;
|
||||
int iaoutidx;
|
||||
int init_oainidx;
|
||||
int oainidx;
|
||||
int oaoutidx;
|
||||
int saveidx;
|
||||
fftw_plan Rfor;
|
||||
fftw_plan Rrev;
|
||||
|
||||
int comp_method;
|
||||
int nfreqs;
|
||||
double* F;
|
||||
double* G;
|
||||
double* E;
|
||||
double* fp;
|
||||
double* gp;
|
||||
double* ep;
|
||||
double* comp;
|
||||
double precomp;
|
||||
double precomplin;
|
||||
double* peq;
|
||||
int peq_run;
|
||||
double prepeq;
|
||||
double prepeqlin;
|
||||
double winfudge;
|
||||
|
||||
double gain;
|
||||
double mtau;
|
||||
double mmult;
|
||||
// display stuff
|
||||
double dtau;
|
||||
double dmult;
|
||||
double* delta;
|
||||
double* delta_copy;
|
||||
double* cfc_gain_copy;
|
||||
|
||||
static CFCOMP* create_cfcomp (
|
||||
int run,
|
||||
int position,
|
||||
int peq_run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int fsize,
|
||||
int ovrlp,
|
||||
int rate,
|
||||
int wintype,
|
||||
int comp_method,
|
||||
int nfreqs,
|
||||
double precomp,
|
||||
double prepeq,
|
||||
double* F,
|
||||
double* G,
|
||||
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, double* in, double* out);
|
||||
static void setSamplerate_cfcomp (CFCOMP *a, int rate);
|
||||
static void setSize_cfcomp (CFCOMP *a, 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, double* F, double* G, double *E);
|
||||
static void SetCFCOMPPrecomp (TXA& txa, double precomp);
|
||||
static void SetCFCOMPPeqRun (TXA& txa, int run);
|
||||
static void SetCFCOMPPrePeq (TXA& txa, double prepeq);
|
||||
static void GetCFCOMPDisplayCompression (TXA& txa, double* comp_values, int* ready);
|
||||
|
||||
private:
|
||||
static void calc_cfcwindow (CFCOMP *a);
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
260
wdsp/cfir.cpp
Normal file
260
wdsp/cfir.cpp
Normal file
@ -0,0 +1,260 @@
|
||||
/* cfir.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 2016, 2021 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 "cfir.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void CFIR::calc_cfir (CFIR *a)
|
||||
{
|
||||
double* impulse;
|
||||
a->scale = 1.0 / (double)(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);
|
||||
}
|
||||
|
||||
void CFIR::decalc_cfir (CFIR *a)
|
||||
{
|
||||
FIRCORE::destroy_fircore (a->p);
|
||||
}
|
||||
|
||||
CFIR* CFIR::create_cfir (int run, int size, int nc, int mp, double* in, double* 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
|
||||
// nc: number of filter coefficients
|
||||
// mp: minimum phase flag
|
||||
// in: pointer to the input buffer
|
||||
// out: pointer to the output buffer
|
||||
// rate: samplerate
|
||||
// DD: differential delay of the CIC to be compensated (usually 1 or 2)
|
||||
// R: interpolation factor of CIC
|
||||
// Pairs: number of comb-integrator pairs in the CIC
|
||||
// cutoff: cutoff frequency
|
||||
// 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;
|
||||
}
|
||||
|
||||
void CFIR::destroy_cfir (CFIR *a)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void CFIR::flush_cfir (CFIR *a)
|
||||
{
|
||||
FIRCORE::flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void CFIR::xcfir (CFIR *a)
|
||||
{
|
||||
if (a->run)
|
||||
FIRCORE::xfircore (a->p);
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void CFIR::setBuffers_cfir (CFIR *a, double* in, double* out)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
void CFIR::setSamplerate_cfir (CFIR *a, int rate)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->runrate = rate;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
void CFIR::setSize_cfir (CFIR *a, int size)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->size = size;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
void CFIR::setOutRate_cfir (CFIR *a, int rate)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->cicrate = rate;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
double* 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)
|
||||
{
|
||||
// N: number of impulse response samples
|
||||
// DD: differential delay used in the CIC filter
|
||||
// R: interpolation / decimation factor of the CIC
|
||||
// Pairs: number of comb-integrator pairs in the CIC
|
||||
// runrate: sample rate at which this filter is to run (assumes there may be flat interp. between this filter and the CIC)
|
||||
// cicrate: sample rate at interface to CIC
|
||||
// cutoff: cutoff frequency
|
||||
// xtype: transition type, 0 for 4th-power rolloff, 1 for raised cosine, 2 for brick wall
|
||||
// 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;
|
||||
double* impulse;
|
||||
double* A = new double[N]; // (double *) malloc0 (N * sizeof (double));
|
||||
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 * (double)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N
|
||||
double* xistion = new double[x_samps + 1]; // (double *) malloc0 ((x_samps + 1) * sizeof (double));
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||
{
|
||||
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)
|
||||
tmp = -tmp;
|
||||
mag = pow (tmp, Pairs) * local_scale;
|
||||
}
|
||||
else
|
||||
mag *= (ft * ft * ft * ft) / (fn * fn * fn * fn);
|
||||
A[i] = mag;
|
||||
}
|
||||
}
|
||||
else if (xtype == 1)
|
||||
{
|
||||
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||
{
|
||||
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)
|
||||
tmp = -tmp;
|
||||
mag = pow (tmp, Pairs) * local_scale;
|
||||
A[i] = mag;
|
||||
}
|
||||
else if ( i >= c_samps && i <= c_samps + x_samps)
|
||||
A[i] = mag * xistion[i - c_samps];
|
||||
else
|
||||
A[i] = 0.0;
|
||||
}
|
||||
}
|
||||
else if (xtype == 2)
|
||||
{
|
||||
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||
{
|
||||
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)
|
||||
tmp = -tmp;
|
||||
mag = pow (tmp, Pairs) * local_scale;
|
||||
}
|
||||
else
|
||||
mag = 0.0;
|
||||
A[i] = mag;
|
||||
}
|
||||
}
|
||||
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++)
|
||||
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);
|
||||
return impulse;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void CFIR::SetCFIRRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.cfir.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void CFIR::SetCFIRNC(TXA& txa, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
CFIR *a;
|
||||
txa.csDSP.lock();
|
||||
a = txa.cfir.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
decalc_cfir(a);
|
||||
calc_cfir(a);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
108
wdsp/cfir.hpp
Normal file
108
wdsp/cfir.hpp
Normal file
@ -0,0 +1,108 @@
|
||||
/* cfir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_cfir_h
|
||||
#define wdsp_cfir_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API CFIR
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
int nc;
|
||||
int mp;
|
||||
double* in;
|
||||
double* out;
|
||||
int runrate;
|
||||
int cicrate;
|
||||
int DD;
|
||||
int R;
|
||||
int Pairs;
|
||||
double cutoff;
|
||||
double scale;
|
||||
int xtype;
|
||||
double xbw;
|
||||
int wintype;
|
||||
FIRCORE *p;
|
||||
|
||||
static CFIR* create_cfir (
|
||||
int run,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* out,
|
||||
int runrate,
|
||||
int cicrate,
|
||||
int DD,
|
||||
int R,
|
||||
int Pairs,
|
||||
double cutoff,
|
||||
int xtype,
|
||||
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, double* in, double* 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 double* 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
|
||||
);
|
||||
// TXA Properties
|
||||
static void SetCFIRRun(TXA& txa, int run);
|
||||
static void SetCFIRNC(TXA& txa, int nc);
|
||||
|
||||
private:
|
||||
static void calc_cfir (CFIR *a);
|
||||
static void decalc_cfir (CFIR *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
95
wdsp/channel.hpp
Normal file
95
wdsp/channel.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
/* channel.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_channel_h
|
||||
#define wdsp_channel_h
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
#include <QThread>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
class WDSP_API Channel
|
||||
{
|
||||
public:
|
||||
int type;
|
||||
bool run; // thread running
|
||||
int in_rate; // input samplerate
|
||||
int out_rate; // output samplerate
|
||||
int in_size; // input buffsize (complex samples) in a fexchange() operation
|
||||
int dsp_rate; // sample rate for mainstream dsp processing
|
||||
int dsp_size; // number complex samples processed per buffer in mainstream dsp processing
|
||||
int dsp_insize; // size (complex samples) of the output of the r1 (input) buffer
|
||||
int dsp_outsize; // size (complex samples) of the input of the r2 (output) buffer
|
||||
int out_size; // output buffsize (complex samples) in a fexchange() operation
|
||||
QRecursiveMutex csDSP; // used to block dsp while parameters are updated or buffers flushed
|
||||
QRecursiveMutex csEXCH; // used to block fexchange() while parameters are updated or buffers flushed
|
||||
int state; // 0 for channel OFF; 1 for channel ON
|
||||
double tdelayup;
|
||||
double tslewup;
|
||||
double tdelaydown;
|
||||
double tslewdown;
|
||||
int bfo; // 'block_for_output', block fexchange until output is available
|
||||
volatile long flushflag;
|
||||
QThread channelThread;
|
||||
std::atomic<long> upslew;
|
||||
// struct //io buffers
|
||||
// {
|
||||
// IOB pc, pd, pe, pf; // copies for console calls, dsp, exchange, and flush thread
|
||||
// volatile long ch_upslew;
|
||||
// } iob;
|
||||
|
||||
static void create_channel (
|
||||
int channel,
|
||||
int in_size,
|
||||
int dsp_size,
|
||||
int input_samplerate,
|
||||
int dsp_rate,
|
||||
int output_samplerate,
|
||||
int type,
|
||||
int state,
|
||||
double tdelayup,
|
||||
double tslewup,
|
||||
double tdelaydown,
|
||||
double tslewdown,
|
||||
int bfo
|
||||
);
|
||||
static void destroy_channel (int channel);
|
||||
static void flush_channel (int channel);
|
||||
// static void set_type (int channel, int type);
|
||||
// static void SetInputBuffsize (int channel, int in_size);
|
||||
// static void SetDSPBuffsize (int channel, int dsp_size);
|
||||
// static void SetInputSamplerate (int channel, int samplerate);
|
||||
// static void SetDSPSamplerate (int channel, int samplerate);
|
||||
// static void SetOutputSamplerate (int channel, int samplerate);
|
||||
// static void SetAllRates (int channel, int in_rate, int dsp_rate, int out_rate);
|
||||
// static int SetChannelState (int channel, int state, int dmode);
|
||||
};
|
||||
|
||||
#endif
|
||||
80
wdsp/comm.hpp
Normal file
80
wdsp/comm.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
/* comm.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#define APPNAME "WDSP"
|
||||
#define LOGD(LOG_TAG, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGI(LOG_TAG, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG,__VA_ARGS__)
|
||||
#define LOGV(LOG_TAG, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(LOG_TAG, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(LOG_TAG, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
// manage differences among consoles
|
||||
#define _Thetis
|
||||
|
||||
// channel definitions
|
||||
#define MAX_CHANNELS 32 // maximum number of supported channels
|
||||
#define DSP_MULT 2 // number of dsp_buffsizes that are held in an iobuff pseudo-ring
|
||||
#define INREAL float // data type for channel input buffer
|
||||
#define OUTREAL float // data type for channel output buffer
|
||||
|
||||
// display definitions
|
||||
#define dMAX_DISPLAYS 64 // maximum number of displays = max instances
|
||||
#define dMAX_STITCH 4 // maximum number of sub-spans to stitch together
|
||||
#define dMAX_NUM_FFT 1 // maximum number of ffts for an elimination
|
||||
#define dMAX_PIXELS 16384 // maximum number of pixels that can be requested
|
||||
#define dMAX_AVERAGE 60 // maximum number of pixel frames that will be window-averaged
|
||||
#ifdef _Thetis
|
||||
#define dINREAL double
|
||||
#else
|
||||
#define dINREAL float
|
||||
#endif
|
||||
#define dOUTREAL float
|
||||
#define dSAMP_BUFF_MULT 2 // ratio of input sample buffer size to fft size (for overlap)
|
||||
#define dNUM_PIXEL_BUFFS 3 // number of pixel output buffers
|
||||
#define dMAX_M 1 // number of variables to calibrate
|
||||
#define dMAX_N 100 // maximum number of frequencies at which to calibrate
|
||||
#define dMAX_CAL_SETS 2 // maximum number of calibration data sets
|
||||
#define dMAX_PIXOUTS 4 // maximum number of det/avg/outputs per display instance
|
||||
|
||||
// wisdom definitions
|
||||
#define MAX_WISDOM_SIZE_DISPLAY 262144
|
||||
#define MAX_WISDOM_SIZE_FILTER 262144 // was 32769
|
||||
|
||||
// math definitions
|
||||
#define PI 3.1415926535897932
|
||||
#define TWOPI 6.2831853071795864
|
||||
|
||||
// miscellaneous
|
||||
typedef double dcomplex[2];
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
119
wdsp/compress.cpp
Normal file
119
wdsp/compress.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/* compress.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2011, 2013, 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
|
||||
|
||||
This software is based upon the algorithm described by Peter Martinez, G3PLX,
|
||||
in the January 2010 issue of RadCom magazine.
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "compress.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
COMPRESSOR* create_compressor (
|
||||
int run,
|
||||
int buffsize,
|
||||
double* inbuff,
|
||||
double* outbuff,
|
||||
double gain )
|
||||
{
|
||||
COMPRESSOR *a = new COMPRESSOR;
|
||||
a->run = run;
|
||||
a->inbuff = inbuff;
|
||||
a->outbuff = outbuff;
|
||||
a->buffsize = buffsize;
|
||||
a->gain = gain;
|
||||
return a;
|
||||
}
|
||||
|
||||
void COMPRESSOR::destroy_compressor (COMPRESSOR *a)
|
||||
{
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void COMPRESSOR::flush_compressor (COMPRESSOR *)
|
||||
{
|
||||
}
|
||||
|
||||
void COMPRESSOR::xcompressor (COMPRESSOR *a)
|
||||
{
|
||||
int i;
|
||||
double mag;
|
||||
if (a->run)
|
||||
for (i = 0; i < a->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;
|
||||
else
|
||||
a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] * a->gain;
|
||||
a->outbuff[2 * i + 1] = 0.0;
|
||||
}
|
||||
else if (a->inbuff != a->outbuff)
|
||||
memcpy(a->outbuff, a->inbuff, a->buffsize * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void COMPRESSOR::setBuffers_compressor (COMPRESSOR *a, double* in, double* out)
|
||||
{
|
||||
a->inbuff = in;
|
||||
a->outbuff = out;
|
||||
}
|
||||
|
||||
void COMPRESSOR::setSamplerate_compressor (COMPRESSOR *, int)
|
||||
{
|
||||
}
|
||||
|
||||
void COMPRESSOR::setSize_compressor (COMPRESSOR *a, int size)
|
||||
{
|
||||
a->buffsize = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void COMPRESSOR::SetCompressorRun (TXA& txa, int run)
|
||||
{
|
||||
if (txa.compressor.p->run != run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.compressor.p->run = run;
|
||||
TXA::SetupBPFilters (txa);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void COMPRESSOR::SetCompressorGain (TXA& txa, double gain)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.compressor.p->gain = pow (10.0, gain / 20.0);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
66
wdsp/compress.hpp
Normal file
66
wdsp/compress.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
/* compress.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2011, 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_compressor_h
|
||||
#define wdsp_compressor_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API COMPRESSOR
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int buffsize;
|
||||
double *inbuff;
|
||||
double *outbuff;
|
||||
double gain;
|
||||
|
||||
static COMPRESSOR* create_compressor (
|
||||
int run,
|
||||
int buffsize,
|
||||
double* inbuff,
|
||||
double* outbuff,
|
||||
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, double* in, double* out);
|
||||
static void setSamplerate_compressor (COMPRESSOR *a, int rate);
|
||||
static void setSize_compressor (COMPRESSOR *a, int size);
|
||||
// TXA Properties
|
||||
static void SetCompressorRun (TXA& txa, int run);
|
||||
static void SetCompressorGain (TXA& txa, double gain);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
141
wdsp/delay.cpp
Normal file
141
wdsp/delay.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/* delay.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2019 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 "delay.hpp"
|
||||
#include "fir.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
DELAY* create_delay (int run, int size, double* in, double* out, int rate, double tdelta, double 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 * (double)a->rate));
|
||||
a->adelta = 1.0 / (a->rate * a->L);
|
||||
a->ft = 0.45 / (double)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, (double)a->L);
|
||||
a->rsize = a->cpp + (WSDEL - 1);
|
||||
a->ring = new double[a->rsize * 2]; // (double *) malloc0 (a->rsize * sizeof (complex));
|
||||
return a;
|
||||
}
|
||||
|
||||
void DELAY::destroy_delay (DELAY *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete[] (a->h);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void DELAY::flush_delay (DELAY *a)
|
||||
{
|
||||
memset (a->ring, 0, a->cpp * sizeof (dcomplex));
|
||||
a->idx_in = 0;
|
||||
}
|
||||
|
||||
void DELAY::xdelay (DELAY *a)
|
||||
{
|
||||
a->cs_update.lock();
|
||||
if (a->run)
|
||||
{
|
||||
int i, j, k, idx, n;
|
||||
double Itmp, Qtmp;
|
||||
for (i = 0; i < a->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];
|
||||
Itmp = 0.0;
|
||||
Qtmp = 0.0;
|
||||
if ((n = a->idx_in + a->snum) >= a->rsize) n -= a->rsize;
|
||||
for (j = 0, k = a->L - 1 - a->phnum; j < a->cpp; j++, k+= a->L)
|
||||
{
|
||||
if ((idx = n + j) >= a->rsize) idx -= a->rsize;
|
||||
Itmp += a->ring[2 * idx + 0] * a->h[k];
|
||||
Qtmp += a->ring[2 * idx + 1] * a->h[k];
|
||||
}
|
||||
a->out[2 * i + 0] = Itmp;
|
||||
a->out[2 * i + 1] = Qtmp;
|
||||
if (--a->idx_in < 0) a->idx_in = a->rsize - 1;
|
||||
}
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void DELAY::SetDelayRun (DELAY *a, int run)
|
||||
{
|
||||
a->cs_update.lock();
|
||||
a->run = run;
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
double DELAY::SetDelayValue (DELAY *a, double tdelay)
|
||||
{
|
||||
double adelay;
|
||||
a->cs_update.lock();
|
||||
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;
|
||||
a->cs_update.unlock();
|
||||
return adelay;
|
||||
}
|
||||
|
||||
void DELAY::SetDelayBuffs (DELAY *a, int size, double* in, double* out)
|
||||
{
|
||||
a->cs_update.lock();
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
79
wdsp/delay.hpp
Normal file
79
wdsp/delay.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
/* delay.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2019 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_delay_h
|
||||
#define wdsp_delay_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#define WSDEL 1025 // number of supported whole sample delays
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API DELAY
|
||||
{
|
||||
public:
|
||||
int run; // run
|
||||
int size; // number of input samples per buffer
|
||||
double* in; // input buffer
|
||||
double* out; // output buffer
|
||||
int rate; // samplerate
|
||||
double tdelta; // delay increment required (seconds)
|
||||
double tdelay; // delay requested (seconds)
|
||||
|
||||
int L; // interpolation factor
|
||||
int ncoef; // number of coefficients
|
||||
int cpp; // coefficients per phase
|
||||
double ft; // normalized cutoff frequency
|
||||
double* 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
|
||||
double* ring; // ring buffer
|
||||
|
||||
double adelta; // actual delay increment
|
||||
double adelay; // actual delay
|
||||
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static DELAY* create_delay (int run, int size, double* in, double* out, int rate, double tdelta, double tdelay);
|
||||
static void destroy_delay (DELAY *a);
|
||||
static void flush_delay (DELAY *a);
|
||||
static void xdelay (DELAY *a);
|
||||
// Properties
|
||||
static void SetDelayRun (DELAY *a, int run);
|
||||
static double SetDelayValue (DELAY *a, double delay); // returns actual delay in seconds
|
||||
static void SetDelayBuffs (DELAY *a, int size, double* in, double* out);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
963
wdsp/emnr.cpp
Normal file
963
wdsp/emnr.cpp
Normal file
@ -0,0 +1,963 @@
|
||||
/* emnr.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015 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 "calculus.hpp"
|
||||
#include "emnr.hpp"
|
||||
#include "amd.hpp"
|
||||
#include "anr.hpp"
|
||||
#include "anf.hpp"
|
||||
#include "snb.hpp"
|
||||
#include "bandpass.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Special Functions *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// MODIFIED BESSEL FUNCTIONS OF THE 0TH AND 1ST ORDERS, Polynomial Approximations
|
||||
// M. Abramowitz and I. Stegun, Eds., "Handbook of Mathematical Functions." Washington, DC: National
|
||||
// Bureau of Standards, 1964.
|
||||
// Shanjie Zhang and Jianming Jin, "Computation of Special Functions." New York, NY, John Wiley and Sons,
|
||||
// Inc., 1996. [Sample code given in FORTRAN]
|
||||
|
||||
double EMNR::bessI0 (double x)
|
||||
{
|
||||
double res, p;
|
||||
if (x == 0.0)
|
||||
res = 1.0;
|
||||
else
|
||||
{
|
||||
if (x < 0.0) x = -x;
|
||||
if (x <= 3.75)
|
||||
{
|
||||
p = x / 3.75;
|
||||
p = p * p;
|
||||
res = ((((( 0.0045813 * p
|
||||
+ 0.0360768) * p
|
||||
+ 0.2659732) * p
|
||||
+ 1.2067492) * p
|
||||
+ 3.0899424) * p
|
||||
+ 3.5156229) * p
|
||||
+ 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 3.75 / x;
|
||||
res = exp (x) / sqrt (x)
|
||||
* (((((((( + 0.00392377 * p
|
||||
- 0.01647633) * p
|
||||
+ 0.02635537) * p
|
||||
- 0.02057706) * p
|
||||
+ 0.00916281) * p
|
||||
- 0.00157565) * p
|
||||
+ 0.00225319) * p
|
||||
+ 0.01328592) * p
|
||||
+ 0.39894228);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
double EMNR::bessI1 (double x)
|
||||
{
|
||||
|
||||
double res, p;
|
||||
if (x == 0.0)
|
||||
res = 0.0;
|
||||
else
|
||||
{
|
||||
if (x < 0.0) x = -x;
|
||||
if (x <= 3.75)
|
||||
{
|
||||
p = x / 3.75;
|
||||
p = p * p;
|
||||
res = x
|
||||
* (((((( 0.00032411 * p
|
||||
+ 0.00301532) * p
|
||||
+ 0.02658733) * p
|
||||
+ 0.15084934) * p
|
||||
+ 0.51498869) * p
|
||||
+ 0.87890594) * p
|
||||
+ 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 3.75 / x;
|
||||
res = exp (x) / sqrt (x)
|
||||
* (((((((( - 0.00420059 * p
|
||||
+ 0.01787654) * p
|
||||
- 0.02895312) * p
|
||||
+ 0.02282967) * p
|
||||
- 0.01031555) * p
|
||||
+ 0.00163801) * p
|
||||
- 0.00362018) * p
|
||||
- 0.03988024) * p
|
||||
+ 0.39894228);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// EXPONENTIAL INTEGRAL, E1(x)
|
||||
// M. Abramowitz and I. Stegun, Eds., "Handbook of Mathematical Functions." Washington, DC: National
|
||||
// Bureau of Standards, 1964.
|
||||
// Shanjie Zhang and Jianming Jin, "Computation of Special Functions." New York, NY, John Wiley and Sons,
|
||||
// Inc., 1996. [Sample code given in FORTRAN]
|
||||
|
||||
double EMNR::e1xb (double x)
|
||||
{
|
||||
double e1, ga, r, t, t0;
|
||||
int k, m;
|
||||
if (x == 0.0)
|
||||
e1 = 1.0e300;
|
||||
else if (x <= 1.0)
|
||||
{
|
||||
e1 = 1.0;
|
||||
r = 1.0;
|
||||
|
||||
for (k = 1; k <= 25; k++)
|
||||
{
|
||||
r = -r * k * x / ((k + 1.0)*(k + 1.0));
|
||||
e1 = e1 + r;
|
||||
if ( fabs (r) <= fabs (e1) * 1.0e-15 )
|
||||
break;
|
||||
}
|
||||
|
||||
ga = 0.5772156649015328;
|
||||
e1 = - ga - log (x) + x * e1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = 20 + (int)(80.0 / x);
|
||||
t0 = 0.0;
|
||||
for (k = m; k >= 1; k--)
|
||||
t0 = (double)k / (1.0 + k / (x + t0));
|
||||
t = 1.0 / (x + t0);
|
||||
e1 = exp (- x) * t;
|
||||
}
|
||||
return e1;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Main Body of Code *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void EMNR::calc_window (EMNR *a)
|
||||
{
|
||||
int i;
|
||||
double arg, sum, inv_coherent_gain;
|
||||
switch (a->wintype)
|
||||
{
|
||||
case 0:
|
||||
arg = 2.0 * PI / (double)a->fsize;
|
||||
sum = 0.0;
|
||||
for (i = 0; i < a->fsize; i++)
|
||||
{
|
||||
a->window[i] = sqrt (0.54 - 0.46 * cos((double)i * arg));
|
||||
sum += a->window[i];
|
||||
}
|
||||
inv_coherent_gain = (double)a->fsize / sum;
|
||||
for (i = 0; i < a->fsize; i++)
|
||||
a->window[i] *= inv_coherent_gain;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EMNR::interpM (double* res, double x, int nvals, double* xvals, double* yvals)
|
||||
{
|
||||
if (x <= xvals[0])
|
||||
*res = yvals[0];
|
||||
else if (x >= xvals[nvals - 1])
|
||||
*res = yvals[nvals - 1];
|
||||
else
|
||||
{
|
||||
int idx = 0;
|
||||
double xllow, xlhigh, frac;
|
||||
while (x >= xvals[idx]) idx++;
|
||||
xllow = log10 (xvals[idx - 1]);
|
||||
xlhigh = log10(xvals[idx]);
|
||||
frac = (log10 (x) - xllow) / (xlhigh - xllow);
|
||||
*res = yvals[idx - 1] + frac * (yvals[idx] - yvals[idx - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void EMNR::calc_emnr(EMNR *a)
|
||||
{
|
||||
int i;
|
||||
double Dvals[18] = { 1.0, 2.0, 5.0, 8.0, 10.0, 15.0, 20.0, 30.0, 40.0,
|
||||
60.0, 80.0, 120.0, 140.0, 160.0, 180.0, 220.0, 260.0, 300.0 };
|
||||
double Mvals[18] = { 0.000, 0.260, 0.480, 0.580, 0.610, 0.668, 0.705, 0.762, 0.800,
|
||||
0.841, 0.865, 0.890, 0.900, 0.910, 0.920, 0.930, 0.935, 0.940 };
|
||||
// double Hvals[18] = { 0.000, 0.150, 0.480, 0.780, 0.980, 1.550, 2.000, 2.300, 2.520,
|
||||
// 3.100, 3.380, 4.150, 4.350, 4.250, 3.900, 4.100, 4.700, 5.000 };
|
||||
a->incr = a->fsize / a->ovrlp;
|
||||
a->gain = a->ogain / a->fsize / (double)a->ovrlp;
|
||||
if (a->fsize > a->bsize)
|
||||
a->iasize = a->fsize;
|
||||
else
|
||||
a->iasize = a->bsize + a->fsize - a->incr;
|
||||
a->iainidx = 0;
|
||||
a->iaoutidx = 0;
|
||||
if (a->fsize > a->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;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->oasize = a->bsize;
|
||||
a->oainidx = a->fsize - a->incr;
|
||||
}
|
||||
a->init_oainidx = a->oainidx;
|
||||
a->oaoutidx = 0;
|
||||
a->msize = a->fsize / 2 + 1;
|
||||
a->window = new double[a->fsize]; // (double *)malloc0(a->fsize * sizeof(double));
|
||||
a->inaccum = new double[a->iasize]; // (double *)malloc0(a->iasize * sizeof(double));
|
||||
a->forfftin = new double[a->fsize]; // (double *)malloc0(a->fsize * sizeof(double));
|
||||
a->forfftout = new double[a->msize * 2]; // (double *)malloc0(a->msize * sizeof(complex));
|
||||
a->mask = new double[a->msize]; // (double *)malloc0(a->msize * sizeof(double));
|
||||
a->revfftin = new double[a->msize * 2]; // (double *)malloc0(a->msize * sizeof(complex));
|
||||
a->revfftout = new double[a->fsize]; // (double *)malloc0(a->fsize * sizeof(double));
|
||||
a->save = new double*[a->ovrlp]; // (double **)malloc0(a->ovrlp * sizeof(double *));
|
||||
for (i = 0; i < a->ovrlp; i++)
|
||||
a->save[i] = new double[a->fsize]; // (double *)malloc0(a->fsize * sizeof(double));
|
||||
a->outaccum = new double[a->oasize]; // (double *)malloc0(a->oasize * sizeof(double));
|
||||
a->nsamps = 0;
|
||||
a->saveidx = 0;
|
||||
a->Rfor = fftw_plan_dft_r2c_1d(a->fsize, a->forfftin, (fftw_complex *)a->forfftout, FFTW_ESTIMATE);
|
||||
a->Rrev = fftw_plan_dft_c2r_1d(a->fsize, (fftw_complex *)a->revfftin, a->revfftout, FFTW_ESTIMATE);
|
||||
calc_window(a);
|
||||
|
||||
a->g.msize = a->msize;
|
||||
a->g.mask = a->mask;
|
||||
a->g.y = a->forfftout;
|
||||
a->g.lambda_y = new double[a->msize]; // (double *)malloc0(a->msize * sizeof(double));
|
||||
a->g.lambda_d = new double[a->msize]; // (double *)malloc0(a->msize * sizeof(double));
|
||||
a->g.prev_gamma = new double[a->msize]; // (double *)malloc0(a->msize * sizeof(double));
|
||||
a->g.prev_mask = new double[a->msize]; // (double *)malloc0(a->msize * sizeof(double));
|
||||
|
||||
a->g.gf1p5 = sqrt(PI) / 2.0;
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.98);
|
||||
a->g.alpha = exp(-a->incr / a->rate / tau);
|
||||
}
|
||||
a->g.eps_floor = 1.0e-300;
|
||||
a->g.gamma_max = 1000.0;
|
||||
a->g.q = 0.2;
|
||||
for (i = 0; i < a->g.msize; i++)
|
||||
{
|
||||
a->g.prev_mask[i] = 1.0;
|
||||
a->g.prev_gamma[i] = 1.0;
|
||||
}
|
||||
a->g.gmax = 10000.0;
|
||||
//
|
||||
a->g.GG = new double[241 * 241]; // (double *)malloc0(241 * 241 * sizeof(double));
|
||||
a->g.GGS = new double[241 * 241]; // (double *)malloc0(241 * 241 * sizeof(double));
|
||||
if ((a->g.fileb = fopen("calculus", "rb")))
|
||||
{
|
||||
std::size_t lgg = fread(a->g.GG, sizeof(double), 241 * 241, a->g.fileb);
|
||||
if (lgg != 241 * 241) {
|
||||
fprintf(stderr, "GG file has an invalid size\n");
|
||||
}
|
||||
std::size_t lggs =fread(a->g.GGS, sizeof(double), 241 * 241, a->g.fileb);
|
||||
if (lggs != 241 * 241) {
|
||||
fprintf(stderr, "GGS file has an invalid size\n");
|
||||
}
|
||||
fclose(a->g.fileb);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (a->g.GG, Calculus::GG, 241 * 241 * sizeof(double));
|
||||
memcpy (a->g.GGS, Calculus::GGS, 241 * 241 * sizeof(double));
|
||||
}
|
||||
//
|
||||
|
||||
a->np.incr = a->incr;
|
||||
a->np.rate = a->rate;
|
||||
a->np.msize = a->msize;
|
||||
a->np.lambda_y = a->g.lambda_y;
|
||||
a->np.lambda_d = a->g.lambda_d;
|
||||
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.7);
|
||||
a->np.alphaCsmooth = exp(-a->np.incr / a->np.rate / tau);
|
||||
}
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.96);
|
||||
a->np.alphaMax = exp(-a->np.incr / a->np.rate / tau);
|
||||
}
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.7);
|
||||
a->np.alphaCmin = exp(-a->np.incr / a->np.rate / tau);
|
||||
}
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.3);
|
||||
a->np.alphaMin_max_value = exp(-a->np.incr / a->np.rate / tau);
|
||||
}
|
||||
a->np.snrq = -a->np.incr / (0.064 * a->np.rate);
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.8);
|
||||
a->np.betamax = exp(-a->np.incr / a->np.rate / tau);
|
||||
}
|
||||
a->np.invQeqMax = 0.5;
|
||||
a->np.av = 2.12;
|
||||
a->np.Dtime = 8.0 * 12.0 * 128.0 / 8000.0;
|
||||
a->np.U = 8;
|
||||
a->np.V = (int)(0.5 + (a->np.Dtime * a->np.rate / (a->np.U * a->np.incr)));
|
||||
if (a->np.V < 4) a->np.V = 4;
|
||||
if ((a->np.U = (int)(0.5 + (a->np.Dtime * a->np.rate / (a->np.V * a->np.incr)))) < 1) a->np.U = 1;
|
||||
a->np.D = a->np.U * a->np.V;
|
||||
interpM(&a->np.MofD, a->np.D, 18, Dvals, Mvals);
|
||||
interpM(&a->np.MofV, a->np.V, 18, Dvals, Mvals);
|
||||
a->np.invQbar_points[0] = 0.03;
|
||||
a->np.invQbar_points[1] = 0.05;
|
||||
a->np.invQbar_points[2] = 0.06;
|
||||
a->np.invQbar_points[3] = 1.0e300;
|
||||
{
|
||||
double db;
|
||||
db = 10.0 * log10(8.0) / (12.0 * 128 / 8000);
|
||||
a->np.nsmax[0] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate);
|
||||
db = 10.0 * log10(4.0) / (12.0 * 128 / 8000);
|
||||
a->np.nsmax[1] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate);
|
||||
db = 10.0 * log10(2.0) / (12.0 * 128 / 8000);
|
||||
a->np.nsmax[2] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate);
|
||||
db = 10.0 * log10(1.2) / (12.0 * 128 / 8000);
|
||||
a->np.nsmax[3] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate);
|
||||
}
|
||||
|
||||
a->np.p = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.alphaOptHat = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.alphaHat = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.sigma2N = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.pbar = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.p2bar = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.Qeq = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.bmin = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.bmin_sub = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.k_mod = new int[a->np.msize]; // (int *)malloc0(a->np.msize * sizeof(int));
|
||||
a->np.actmin = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.actmin_sub = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.lmin_flag = new int[a->np.msize]; // (int *)malloc0(a->np.msize * sizeof(int));
|
||||
a->np.pmin_u = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
a->np.actminbuff = new double*[a->np.U]; // (double**)malloc0(a->np.U * sizeof(double*));
|
||||
for (i = 0; i < a->np.U; i++)
|
||||
a->np.actminbuff[i] = new double[a->np.msize]; // (double *)malloc0(a->np.msize * sizeof(double));
|
||||
|
||||
{
|
||||
int k, ku;
|
||||
a->np.alphaC = 1.0;
|
||||
a->np.subwc = a->np.V;
|
||||
a->np.amb_idx = 0;
|
||||
for (k = 0; k < a->np.msize; k++) a->np.lambda_y[k] = 0.5;
|
||||
memcpy(a->np.p, a->np.lambda_y, a->np.msize * sizeof(double));
|
||||
memcpy(a->np.sigma2N, a->np.lambda_y, a->np.msize * sizeof(double));
|
||||
memcpy(a->np.pbar, a->np.lambda_y, a->np.msize * sizeof(double));
|
||||
memcpy(a->np.pmin_u, a->np.lambda_y, a->np.msize * sizeof(double));
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
a->np.p2bar[k] = a->np.lambda_y[k] * a->np.lambda_y[k];
|
||||
a->np.actmin[k] = 1.0e300;
|
||||
a->np.actmin_sub[k] = 1.0e300;
|
||||
for (ku = 0; ku < a->np.U; ku++)
|
||||
a->np.actminbuff[ku][k] = 1.0e300;
|
||||
}
|
||||
memset(a->np.lmin_flag, 0, a->np.msize * sizeof(int));
|
||||
}
|
||||
|
||||
a->nps.incr = a->incr;
|
||||
a->nps.rate = a->rate;
|
||||
a->nps.msize = a->msize;
|
||||
a->nps.lambda_y = a->g.lambda_y;
|
||||
a->nps.lambda_d = a->g.lambda_d;
|
||||
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.8);
|
||||
a->nps.alpha_pow = exp(-a->nps.incr / a->nps.rate / tau);
|
||||
}
|
||||
{
|
||||
double tau = -128.0 / 8000.0 / log(0.9);
|
||||
a->nps.alpha_Pbar = exp(-a->nps.incr / a->nps.rate / tau);
|
||||
}
|
||||
a->nps.epsH1 = pow(10.0, 15.0 / 10.0);
|
||||
a->nps.epsH1r = a->nps.epsH1 / (1.0 + a->nps.epsH1);
|
||||
|
||||
a->nps.sigma2N = new double[a->nps.msize]; // (double *)malloc0(a->nps.msize * sizeof(double));
|
||||
a->nps.PH1y = new double[a->nps.msize]; // (double *)malloc0(a->nps.msize * sizeof(double));
|
||||
a->nps.Pbar = new double[a->nps.msize]; // (double *)malloc0(a->nps.msize * sizeof(double));
|
||||
a->nps.EN2y = new double[a->nps.msize]; // (double *)malloc0(a->nps.msize * sizeof(double));
|
||||
|
||||
for (i = 0; i < a->nps.msize; i++)
|
||||
{
|
||||
a->nps.sigma2N[i] = 0.5;
|
||||
a->nps.Pbar[i] = 0.5;
|
||||
}
|
||||
|
||||
a->ae.msize = a->msize;
|
||||
a->ae.lambda_y = a->g.lambda_y;
|
||||
|
||||
a->ae.zetaThresh = 0.75;
|
||||
a->ae.psi = 10.0;
|
||||
|
||||
a->ae.nmask = new double[a->ae.msize]; // (double *)malloc0(a->ae.msize * sizeof(double));
|
||||
}
|
||||
|
||||
void EMNR::decalc_emnr(EMNR *a)
|
||||
{
|
||||
int i;
|
||||
delete[] (a->ae.nmask);
|
||||
|
||||
delete[] (a->nps.EN2y);
|
||||
delete[] (a->nps.Pbar);
|
||||
delete[] (a->nps.PH1y);
|
||||
delete[] (a->nps.sigma2N);
|
||||
|
||||
for (i = 0; i < a->np.U; i++)
|
||||
delete[] (a->np.actminbuff[i]);
|
||||
delete[] (a->np.actminbuff);
|
||||
delete[] (a->np.pmin_u);
|
||||
delete[] (a->np.lmin_flag);
|
||||
delete[] (a->np.actmin_sub);
|
||||
delete[] (a->np.actmin);
|
||||
delete[] (a->np.k_mod);
|
||||
delete[] (a->np.bmin_sub);
|
||||
delete[] (a->np.bmin);
|
||||
delete[] (a->np.Qeq);
|
||||
delete[] (a->np.p2bar);
|
||||
delete[] (a->np.pbar);
|
||||
delete[] (a->np.sigma2N);
|
||||
delete[] (a->np.alphaHat);
|
||||
delete[] (a->np.alphaOptHat);
|
||||
delete[] (a->np.p);
|
||||
|
||||
delete[] (a->g.GGS);
|
||||
delete[] (a->g.GG);
|
||||
delete[] (a->g.prev_mask);
|
||||
delete[] (a->g.prev_gamma);
|
||||
delete[] (a->g.lambda_d);
|
||||
delete[] (a->g.lambda_y);
|
||||
|
||||
fftw_destroy_plan(a->Rrev);
|
||||
fftw_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->mask);
|
||||
delete[] (a->forfftout);
|
||||
delete[] (a->forfftin);
|
||||
delete[] (a->inaccum);
|
||||
delete[] (a->window);
|
||||
}
|
||||
|
||||
EMNR* EMNR::create_emnr (int run, int position, int size, double* in, double* out, int fsize, int ovrlp,
|
||||
int rate, int wintype, double gain, int gain_method, int npe_method, int ae_run)
|
||||
{
|
||||
EMNR *a = new EMNR;
|
||||
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->bsize = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->fsize = fsize;
|
||||
a->ovrlp = ovrlp;
|
||||
a->rate = rate;
|
||||
a->wintype = wintype;
|
||||
a->ogain = gain;
|
||||
a->g.gain_method = gain_method;
|
||||
a->g.npe_method = npe_method;
|
||||
a->g.ae_run = ae_run;
|
||||
calc_emnr (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void EMNR::flush_emnr (EMNR *a)
|
||||
{
|
||||
int i;
|
||||
memset (a->inaccum, 0, a->iasize * sizeof (double));
|
||||
for (i = 0; i < a->ovrlp; i++)
|
||||
memset (a->save[i], 0, a->fsize * sizeof (double));
|
||||
memset (a->outaccum, 0, a->oasize * sizeof (double));
|
||||
a->nsamps = 0;
|
||||
a->iainidx = 0;
|
||||
a->iaoutidx = 0;
|
||||
a->oainidx = a->init_oainidx;
|
||||
a->oaoutidx = 0;
|
||||
a->saveidx = 0;
|
||||
}
|
||||
|
||||
void EMNR::destroy_emnr (EMNR *a)
|
||||
{
|
||||
decalc_emnr (a);
|
||||
delete[] (a);
|
||||
}
|
||||
|
||||
void EMNR::LambdaD(EMNR *a)
|
||||
{
|
||||
int k;
|
||||
double f0, f1, f2, f3;
|
||||
double sum_prev_p;
|
||||
double sum_lambda_y;
|
||||
double alphaCtilda;
|
||||
double sum_prev_sigma2N;
|
||||
double alphaMin, SNR;
|
||||
double beta, varHat, invQeq;
|
||||
double invQbar;
|
||||
double bc;
|
||||
double QeqTilda, QeqTildaSub;
|
||||
double noise_slope_max;
|
||||
|
||||
sum_prev_p = 0.0;
|
||||
sum_lambda_y = 0.0;
|
||||
sum_prev_sigma2N = 0.0;
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
sum_prev_p += a->np.p[k];
|
||||
sum_lambda_y += a->np.lambda_y[k];
|
||||
sum_prev_sigma2N += a->np.sigma2N[k];
|
||||
}
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
f0 = a->np.p[k] / a->np.sigma2N[k] - 1.0;
|
||||
a->np.alphaOptHat[k] = 1.0 / (1.0 + f0 * f0);
|
||||
}
|
||||
SNR = sum_prev_p / sum_prev_sigma2N;
|
||||
alphaMin = std::min (a->np.alphaMin_max_value, pow (SNR, a->np.snrq));
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
if (a->np.alphaOptHat[k] < alphaMin) a->np.alphaOptHat[k] = alphaMin;
|
||||
f1 = sum_prev_p / sum_lambda_y - 1.0;
|
||||
alphaCtilda = 1.0 / (1.0 + f1 * f1);
|
||||
a->np.alphaC = a->np.alphaCsmooth * a->np.alphaC + (1.0 - a->np.alphaCsmooth) * std::max (alphaCtilda, a->np.alphaCmin);
|
||||
f2 = a->np.alphaMax * a->np.alphaC;
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
a->np.alphaHat[k] = f2 * a->np.alphaOptHat[k];
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
a->np.p[k] = a->np.alphaHat[k] * a->np.p[k] + (1.0 - a->np.alphaHat[k]) * a->np.lambda_y[k];
|
||||
invQbar = 0.0;
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
beta = std::min (a->np.betamax, a->np.alphaHat[k] * a->np.alphaHat[k]);
|
||||
a->np.pbar[k] = beta * a->np.pbar[k] + (1.0 - beta) * a->np.p[k];
|
||||
a->np.p2bar[k] = beta * a->np.p2bar[k] + (1.0 - beta) * a->np.p[k] * a->np.p[k];
|
||||
varHat = a->np.p2bar[k] - a->np.pbar[k] * a->np.pbar[k];
|
||||
invQeq = varHat / (2.0 * a->np.sigma2N[k] * a->np.sigma2N[k]);
|
||||
if (invQeq > a->np.invQeqMax) invQeq = a->np.invQeqMax;
|
||||
a->np.Qeq[k] = 1.0 / invQeq;
|
||||
invQbar += invQeq;
|
||||
}
|
||||
invQbar /= (double)a->np.msize;
|
||||
bc = 1.0 + a->np.av * sqrt (invQbar);
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
QeqTilda = (a->np.Qeq[k] - 2.0 * a->np.MofD) / (1.0 - a->np.MofD);
|
||||
QeqTildaSub = (a->np.Qeq[k] - 2.0 * a->np.MofV) / (1.0 - a->np.MofV);
|
||||
a->np.bmin[k] = 1.0 + 2.0 * (a->np.D - 1.0) / QeqTilda;
|
||||
a->np.bmin_sub[k] = 1.0 + 2.0 * (a->np.V - 1.0) / QeqTildaSub;
|
||||
}
|
||||
memset (a->np.k_mod, 0, a->np.msize * sizeof (int));
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
f3 = a->np.p[k] * a->np.bmin[k] * bc;
|
||||
if (f3 < a->np.actmin[k])
|
||||
{
|
||||
a->np.actmin[k] = f3;
|
||||
a->np.actmin_sub[k] = a->np.p[k] * a->np.bmin_sub[k] * bc;
|
||||
a->np.k_mod[k] = 1;
|
||||
}
|
||||
}
|
||||
if (a->np.subwc == a->np.V)
|
||||
{
|
||||
if (invQbar < a->np.invQbar_points[0]) noise_slope_max = a->np.nsmax[0];
|
||||
else if (invQbar < a->np.invQbar_points[1]) noise_slope_max = a->np.nsmax[1];
|
||||
else if (invQbar < a->np.invQbar_points[2]) noise_slope_max = a->np.nsmax[2];
|
||||
else noise_slope_max = a->np.nsmax[3];
|
||||
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
int ku;
|
||||
double min;
|
||||
if (a->np.k_mod[k])
|
||||
a->np.lmin_flag[k] = 0;
|
||||
a->np.actminbuff[a->np.amb_idx][k] = a->np.actmin[k];
|
||||
min = 1.0e300;
|
||||
for (ku = 0; ku < a->np.U; ku++)
|
||||
if (a->np.actminbuff[ku][k] < min) min = a->np.actminbuff[ku][k];
|
||||
a->np.pmin_u[k] = min;
|
||||
if ((a->np.lmin_flag[k] == 1)
|
||||
&& (a->np.actmin_sub[k] < noise_slope_max * a->np.pmin_u[k])
|
||||
&& (a->np.actmin_sub[k] > a->np.pmin_u[k]))
|
||||
{
|
||||
a->np.pmin_u[k] = a->np.actmin_sub[k];
|
||||
for (ku = 0; ku < a->np.U; ku++)
|
||||
a->np.actminbuff[ku][k] = a->np.actmin_sub[k];
|
||||
}
|
||||
a->np.lmin_flag[k] = 0;
|
||||
a->np.actmin[k] = 1.0e300;
|
||||
a->np.actmin_sub[k] = 1.0e300;
|
||||
}
|
||||
if (++a->np.amb_idx == a->np.U) a->np.amb_idx = 0;
|
||||
a->np.subwc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a->np.subwc > 1)
|
||||
{
|
||||
for (k = 0; k < a->np.msize; k++)
|
||||
{
|
||||
if (a->np.k_mod[k])
|
||||
{
|
||||
a->np.lmin_flag[k] = 1;
|
||||
a->np.sigma2N[k] = std::min (a->np.actmin_sub[k], a->np.pmin_u[k]);
|
||||
a->np.pmin_u[k] = a->np.sigma2N[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
++a->np.subwc;
|
||||
}
|
||||
memcpy (a->np.lambda_d, a->np.sigma2N, a->np.msize * sizeof (double));
|
||||
}
|
||||
|
||||
void EMNR::LambdaDs (EMNR *a)
|
||||
{
|
||||
int k;
|
||||
for (k = 0; k < a->nps.msize; k++)
|
||||
{
|
||||
a->nps.PH1y[k] = 1.0 / (1.0 + (1.0 + a->nps.epsH1) * exp (- a->nps.epsH1r * a->nps.lambda_y[k] / a->nps.sigma2N[k]));
|
||||
a->nps.Pbar[k] = a->nps.alpha_Pbar * a->nps.Pbar[k] + (1.0 - a->nps.alpha_Pbar) * a->nps.PH1y[k];
|
||||
if (a->nps.Pbar[k] > 0.99)
|
||||
a->nps.PH1y[k] = std::min (a->nps.PH1y[k], 0.99);
|
||||
a->nps.EN2y[k] = (1.0 - a->nps.PH1y[k]) * a->nps.lambda_y[k] + a->nps.PH1y[k] * a->nps.sigma2N[k];
|
||||
a->nps.sigma2N[k] = a->nps.alpha_pow * a->nps.sigma2N[k] + (1.0 - a->nps.alpha_pow) * a->nps.EN2y[k];
|
||||
}
|
||||
memcpy (a->nps.lambda_d, a->nps.sigma2N, a->nps.msize * sizeof (double));
|
||||
}
|
||||
|
||||
void EMNR::aepf(EMNR *a)
|
||||
{
|
||||
int k, m;
|
||||
int N, n;
|
||||
double sumPre, sumPost, zeta, zetaT;
|
||||
sumPre = 0.0;
|
||||
sumPost = 0.0;
|
||||
for (k = 0; k < a->ae.msize; k++)
|
||||
{
|
||||
sumPre += a->ae.lambda_y[k];
|
||||
sumPost += a->mask[k] * a->mask[k] * a->ae.lambda_y[k];
|
||||
}
|
||||
zeta = sumPost / sumPre;
|
||||
if (zeta >= a->ae.zetaThresh)
|
||||
zetaT = 1.0;
|
||||
else
|
||||
zetaT = zeta;
|
||||
if (zetaT == 1.0)
|
||||
N = 1;
|
||||
else
|
||||
N = 1 + 2 * (int)(0.5 + a->ae.psi * (1.0 - zetaT / a->ae.zetaThresh));
|
||||
n = N / 2;
|
||||
for (k = n; k < (a->ae.msize - n); k++)
|
||||
{
|
||||
a->ae.nmask[k] = 0.0;
|
||||
for (m = k - n; m <= (k + n); m++)
|
||||
a->ae.nmask[k] += a->mask[m];
|
||||
a->ae.nmask[k] /= (double)N;
|
||||
}
|
||||
memcpy (a->mask + n, a->ae.nmask, (a->ae.msize - 2 * n) * sizeof (double));
|
||||
}
|
||||
|
||||
double EMNR::getKey(double* type, double gamma, double xi)
|
||||
{
|
||||
int ngamma1, ngamma2, nxi1, nxi2;
|
||||
double tg, tx, dg, dx;
|
||||
const double dmin = 0.001;
|
||||
const double dmax = 1000.0;
|
||||
if (gamma <= dmin)
|
||||
{
|
||||
ngamma1 = ngamma2 = 0;
|
||||
tg = 0.0;
|
||||
}
|
||||
else if (gamma >= dmax)
|
||||
{
|
||||
ngamma1 = ngamma2 = 240;
|
||||
tg = 60.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tg = 10.0 * log10(gamma / dmin);
|
||||
ngamma1 = (int)(4.0 * tg);
|
||||
ngamma2 = ngamma1 + 1;
|
||||
}
|
||||
if (xi <= dmin)
|
||||
{
|
||||
nxi1 = nxi2 = 0;
|
||||
tx = 0.0;
|
||||
}
|
||||
else if (xi >= dmax)
|
||||
{
|
||||
nxi1 = nxi2 = 240;
|
||||
tx = 60.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tx = 10.0 * log10(xi / dmin);
|
||||
nxi1 = (int)(4.0 * tx);
|
||||
nxi2 = nxi1 + 1;
|
||||
}
|
||||
dg = (tg - 0.25 * ngamma1) / 0.25;
|
||||
dx = (tx - 0.25 * nxi1) / 0.25;
|
||||
return (1.0 - dg) * (1.0 - dx) * type[241 * nxi1 + ngamma1]
|
||||
+ (1.0 - dg) * dx * type[241 * nxi2 + ngamma1]
|
||||
+ dg * (1.0 - dx) * type[241 * nxi1 + ngamma2]
|
||||
+ dg * dx * type[241 * nxi2 + ngamma2];
|
||||
}
|
||||
|
||||
void EMNR::calc_gain (EMNR *a)
|
||||
{
|
||||
int k;
|
||||
for (k = 0; k < a->g.msize; k++)
|
||||
{
|
||||
a->g.lambda_y[k] = a->g.y[2 * k + 0] * a->g.y[2 * k + 0] + a->g.y[2 * k + 1] * a->g.y[2 * k + 1];
|
||||
}
|
||||
switch (a->g.npe_method)
|
||||
{
|
||||
case 0:
|
||||
LambdaD(a);
|
||||
break;
|
||||
case 1:
|
||||
LambdaDs(a);
|
||||
break;
|
||||
}
|
||||
switch (a->g.gain_method)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
double gamma, eps_hat, v;
|
||||
for (k = 0; k < a->msize; k++)
|
||||
{
|
||||
gamma = std::min (a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max);
|
||||
eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k]
|
||||
+ (1.0 - a->g.alpha) * std::max (gamma - 1.0, a->g.eps_floor);
|
||||
v = (eps_hat / (1.0 + eps_hat)) * gamma;
|
||||
a->g.mask[k] = a->g.gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v)
|
||||
* ((1.0 + v) * bessI0 (0.5 * v) + v * bessI1 (0.5 * v));
|
||||
{
|
||||
double v2 = std::min (v, 700.0);
|
||||
double eta = a->g.mask[k] * a->g.mask[k] * a->g.lambda_y[k] / a->g.lambda_d[k];
|
||||
double eps = eta / (1.0 - a->g.q);
|
||||
double witchHat = (1.0 - a->g.q) / a->g.q * exp (v2) / (1.0 + eps);
|
||||
a->g.mask[k] *= witchHat / (1.0 + witchHat);
|
||||
}
|
||||
if (a->g.mask[k] > a->g.gmax) a->g.mask[k] = a->g.gmax;
|
||||
if (a->g.mask[k] != a->g.mask[k]) a->g.mask[k] = 0.01;
|
||||
a->g.prev_gamma[k] = gamma;
|
||||
a->g.prev_mask[k] = a->g.mask[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
double gamma, eps_hat, v, ehr;
|
||||
for (k = 0; k < a->g.msize; k++)
|
||||
{
|
||||
gamma = std::min (a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max);
|
||||
eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k]
|
||||
+ (1.0 - a->g.alpha) * std::max (gamma - 1.0, a->g.eps_floor);
|
||||
ehr = eps_hat / (1.0 + eps_hat);
|
||||
v = ehr * gamma;
|
||||
if((a->g.mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > a->g.gmax) a->g.mask[k] = a->g.gmax;
|
||||
if (a->g.mask[k] != a->g.mask[k])a->g.mask[k] = 0.01;
|
||||
a->g.prev_gamma[k] = gamma;
|
||||
a->g.prev_mask[k] = a->g.mask[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
double gamma, eps_hat, eps_p;
|
||||
for (k = 0; k < a->msize; k++)
|
||||
{
|
||||
gamma = std::min(a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max);
|
||||
eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k]
|
||||
+ (1.0 - a->g.alpha) * std::max(gamma - 1.0, a->g.eps_floor);
|
||||
eps_p = eps_hat / (1.0 - a->g.q);
|
||||
a->g.mask[k] = getKey(a->g.GG, gamma, eps_hat) * getKey(a->g.GGS, gamma, eps_p);
|
||||
a->g.prev_gamma[k] = gamma;
|
||||
a->g.prev_mask[k] = a->g.mask[k];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (a->g.ae_run) aepf(a);
|
||||
}
|
||||
|
||||
void EMNR::xemnr (EMNR *a, int pos)
|
||||
{
|
||||
if (a->run && pos == a->position)
|
||||
{
|
||||
int i, j, k, sbuff, sbegin;
|
||||
double g1;
|
||||
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->window[i] * a->inaccum[j];
|
||||
a->iaoutidx = (a->iaoutidx + a->incr) % a->iasize;
|
||||
a->nsamps -= a->incr;
|
||||
fftw_execute (a->Rfor);
|
||||
calc_gain(a);
|
||||
for (i = 0; i < a->msize; i++)
|
||||
{
|
||||
g1 = a->gain * a->mask[i];
|
||||
a->revfftin[2 * i + 0] = g1 * a->forfftout[2 * i + 0];
|
||||
a->revfftin[2 * i + 1] = g1 * a->forfftout[2 * i + 1];
|
||||
}
|
||||
fftw_execute (a->Rrev);
|
||||
for (i = 0; i < a->fsize; i++)
|
||||
a->save[a->saveidx][i] = 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)
|
||||
{
|
||||
if ( i == a->ovrlp)
|
||||
a->outaccum[k] = a->save[sbuff][j];
|
||||
else
|
||||
a->outaccum[k] += a->save[sbuff][j];
|
||||
}
|
||||
}
|
||||
a->saveidx = (a->saveidx + 1) % a->ovrlp;
|
||||
a->oainidx = (a->oainidx + a->incr) % a->oasize;
|
||||
}
|
||||
for (i = 0; i < a->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;
|
||||
}
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->bsize * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EMNR::setBuffers_emnr (EMNR *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void EMNR::setSamplerate_emnr (EMNR *a, int rate)
|
||||
{
|
||||
decalc_emnr (a);
|
||||
a->rate = rate;
|
||||
calc_emnr (a);
|
||||
}
|
||||
|
||||
void EMNR::setSize_emnr (EMNR *a, int size)
|
||||
{
|
||||
decalc_emnr (a);
|
||||
a->bsize = size;
|
||||
calc_emnr (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void EMNR::SetEMNRRun (RXA& rxa, int run)
|
||||
{
|
||||
EMNR *a = rxa.emnr.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
RXA::bp1Check (rxa, rxa.amd.p->run, rxa.snba.p->run,
|
||||
run, rxa.anf.p->run, rxa.anr.p->run);
|
||||
rxa.csDSP.lock();
|
||||
a->run = run;
|
||||
RXA::bp1Set (rxa);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void EMNR::SetEMNRgainMethod (RXA& rxa, int method)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.emnr.p->g.gain_method = method;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EMNR::SetEMNRnpeMethod (RXA& rxa, int method)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.emnr.p->g.npe_method = method;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EMNR::SetEMNRaeRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.emnr.p->g.ae_run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EMNR::SetEMNRPosition (RXA& rxa, int position)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.emnr.p->position = position;
|
||||
rxa.bp1.p->position = position;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EMNR::SetEMNRaeZetaThresh (RXA& rxa, double zetathresh)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.emnr.p->ae.zetaThresh = zetathresh;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EMNR::SetEMNRaePsi (RXA& rxa, double psi)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.emnr.p->ae.psi = psi;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
200
wdsp/emnr.hpp
Normal file
200
wdsp/emnr.hpp
Normal file
@ -0,0 +1,200 @@
|
||||
/* emnr.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_emnr_h
|
||||
#define wdsp_emnr_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API EMNR
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int bsize;
|
||||
double* in;
|
||||
double* out;
|
||||
int fsize;
|
||||
int ovrlp;
|
||||
int incr;
|
||||
double* window;
|
||||
int iasize;
|
||||
double* inaccum;
|
||||
double* forfftin;
|
||||
double* forfftout;
|
||||
int msize;
|
||||
double* mask;
|
||||
double* revfftin;
|
||||
double* revfftout;
|
||||
double** save;
|
||||
int oasize;
|
||||
double* outaccum;
|
||||
double rate;
|
||||
int wintype;
|
||||
double ogain;
|
||||
double gain;
|
||||
int nsamps;
|
||||
int iainidx;
|
||||
int iaoutidx;
|
||||
int init_oainidx;
|
||||
int oainidx;
|
||||
int oaoutidx;
|
||||
int saveidx;
|
||||
fftw_plan Rfor;
|
||||
fftw_plan Rrev;
|
||||
struct _g
|
||||
{
|
||||
int gain_method;
|
||||
int npe_method;
|
||||
int ae_run;
|
||||
double msize;
|
||||
double* mask;
|
||||
double* y;
|
||||
double* lambda_y;
|
||||
double* lambda_d;
|
||||
double* prev_mask;
|
||||
double* prev_gamma;
|
||||
double gf1p5;
|
||||
double alpha;
|
||||
double eps_floor;
|
||||
double gamma_max;
|
||||
double q;
|
||||
double gmax;
|
||||
//
|
||||
double* GG;
|
||||
double* GGS;
|
||||
FILE* fileb;
|
||||
} g;
|
||||
struct _npest
|
||||
{
|
||||
int incr;
|
||||
double rate;
|
||||
int msize;
|
||||
double* lambda_y;
|
||||
double* lambda_d;
|
||||
double* p;
|
||||
double* alphaOptHat;
|
||||
double alphaC;
|
||||
double alphaCsmooth;
|
||||
double alphaCmin;
|
||||
double* alphaHat;
|
||||
double alphaMax;
|
||||
double* sigma2N;
|
||||
double alphaMin_max_value;
|
||||
double snrq;
|
||||
double betamax;
|
||||
double* pbar;
|
||||
double* p2bar;
|
||||
double invQeqMax;
|
||||
double av;
|
||||
double* Qeq;
|
||||
int U;
|
||||
double Dtime;
|
||||
int V;
|
||||
int D;
|
||||
double MofD;
|
||||
double MofV;
|
||||
double* bmin;
|
||||
double* bmin_sub;
|
||||
int* k_mod;
|
||||
double* actmin;
|
||||
double* actmin_sub;
|
||||
int subwc;
|
||||
int* lmin_flag;
|
||||
double* pmin_u;
|
||||
double invQbar_points[4];
|
||||
double nsmax[4];
|
||||
double** actminbuff;
|
||||
int amb_idx;
|
||||
} np;
|
||||
struct _npests
|
||||
{
|
||||
int incr;
|
||||
double rate;
|
||||
int msize;
|
||||
double* lambda_y;
|
||||
double* lambda_d;
|
||||
|
||||
double alpha_pow;
|
||||
double alpha_Pbar;
|
||||
double epsH1;
|
||||
double epsH1r;
|
||||
|
||||
double* sigma2N;
|
||||
double* PH1y;
|
||||
double* Pbar;
|
||||
double* EN2y;
|
||||
} nps;
|
||||
struct _ae
|
||||
{
|
||||
int msize;
|
||||
double* lambda_y;
|
||||
double zetaThresh;
|
||||
double psi;
|
||||
double* nmask;
|
||||
} ae;
|
||||
|
||||
static EMNR* create_emnr (int run, int position, int size, double* in, double* out, int fsize, int ovrlp,
|
||||
int rate, int wintype, double gain, int gain_method, 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, double* in, double* 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);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
275
wdsp/emph.cpp
Normal file
275
wdsp/emph.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/* 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 "emph.hpp"
|
||||
#include "fcurve.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save FM Pre-Emphasis *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
EMPHP* EMPHP::create_emphp (int run, int position, int size, int nc, int mp, double* in, double* out, int rate, int ctype, double f_low, double f_high)
|
||||
{
|
||||
EMPHP *a = new EMPHP;
|
||||
double* 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)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EMPHP::setBuffers_emphp (EMPHP *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void EMPHP::setSamplerate_emphp (EMPHP *a, int rate)
|
||||
{
|
||||
double* 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)
|
||||
{
|
||||
double* 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.csDSP.lock();
|
||||
txa.preemph.p->position = position;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
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;
|
||||
double* impulse;
|
||||
txa.csDSP.lock();
|
||||
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);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EMPHP::SetFMPreEmphFreqs (TXA& txa, double low, double high)
|
||||
{
|
||||
EMPHP *a;
|
||||
double* impulse;
|
||||
txa.csDSP.lock();
|
||||
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);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save FM Pre-Emphasis *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void EMPH::calc_emph (EMPH *a)
|
||||
{
|
||||
a->infilt = new double[2 * a->size * 2]; // (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->product = new double[2 * a->size * 2]; // (double *)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 = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->infilt, (fftw_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->CRev = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->product, (fftw_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
}
|
||||
|
||||
void EMPH::decalc_emph (EMPH *a)
|
||||
{
|
||||
fftw_destroy_plan(a->CRev);
|
||||
fftw_destroy_plan(a->CFor);
|
||||
delete[] (a->mults);
|
||||
delete[] (a->product);
|
||||
delete[] (a->infilt);
|
||||
}
|
||||
|
||||
EMPH* EMPH::create_emph (int run, int position, int size, double* in, double* out, int rate, int ctype, double f_low, double f_high)
|
||||
{
|
||||
EMPH *a = new EMPH;
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = (double)rate;
|
||||
a->ctype = ctype;
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
calc_emph (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void EMPH::destroy_emph (EMPH *a)
|
||||
{
|
||||
decalc_emph (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void EMPH::flush_emph (EMPH *a)
|
||||
{
|
||||
memset (a->infilt, 0, 2 * a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EMPH::xemph (EMPH *a, int position)
|
||||
{
|
||||
int i;
|
||||
double I, Q;
|
||||
if (a->run && a->position == position)
|
||||
{
|
||||
memcpy (&(a->infilt[2 * a->size]), a->in, a->size * sizeof (dcomplex));
|
||||
fftw_execute (a->CFor);
|
||||
for (i = 0; i < 2 * a->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];
|
||||
}
|
||||
fftw_execute (a->CRev);
|
||||
memcpy (a->infilt, &(a->infilt[2 * a->size]), a->size * sizeof(dcomplex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EMPH::setBuffers_emph (EMPH *a, double* in, double* out)
|
||||
{
|
||||
decalc_emph (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_emph (a);
|
||||
}
|
||||
|
||||
void EMPH::setSamplerate_emph (EMPH *a, int rate)
|
||||
{
|
||||
decalc_emph (a);
|
||||
a->rate = rate;
|
||||
calc_emph (a);
|
||||
}
|
||||
|
||||
void EMPH::setSize_emph (EMPH *a, int size)
|
||||
{
|
||||
decalc_emph(a);
|
||||
a->size = size;
|
||||
calc_emph(a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* 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
|
||||
125
wdsp/emph.hpp
Normal file
125
wdsp/emph.hpp
Normal file
@ -0,0 +1,125 @@
|
||||
/* 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;
|
||||
double* in;
|
||||
double* out;
|
||||
int ctype;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double rate;
|
||||
FIRCORE *p;
|
||||
|
||||
static EMPHP* create_emphp (int run, int position, int size, int nc, int mp,
|
||||
double* in, double* out, int rate, int ctype, double f_low, double 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, double* in, double* 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, double low, double high);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save FM Pre-Emphasis *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _emph_h
|
||||
#define _emph_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API EMPH
|
||||
{
|
||||
int run;
|
||||
int position;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
int ctype;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double* infilt;
|
||||
double* product;
|
||||
double* mults;
|
||||
double rate;
|
||||
fftw_plan CFor;
|
||||
fftw_plan CRev;
|
||||
|
||||
static EMPH* create_emph (int run, int position, int size, double* in, double* out, int rate, int ctype, double f_low, double 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, double* in, double* out);
|
||||
static void setSamplerate_emph (EMPH *a, int rate);
|
||||
static void setSize_emph (EMPH *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_emph (EMPH *a);
|
||||
static void decalc_emph (EMPH *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
860
wdsp/eq.cpp
Normal file
860
wdsp/eq.cpp
Normal file
@ -0,0 +1,860 @@
|
||||
/* 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 "eq.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
int EQP::fEQcompare (const void * a, const void * b)
|
||||
{
|
||||
if (*(double*)a < *(double*)b)
|
||||
return -1;
|
||||
else if (*(double*)a == *(double*)b)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
double* EQP::eq_impulse (int N, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype)
|
||||
{
|
||||
double* fp = new double[nfreqs + 2]; // (double *) malloc0 ((nfreqs + 2) * sizeof (double));
|
||||
double* gp = new double[nfreqs + 2]; // (double *) malloc0 ((nfreqs + 2) * sizeof (double));
|
||||
double* A = new double[N / 2 + 1]; // (double *) malloc0 ((N / 2 + 1) * sizeof (double));
|
||||
double* sary = new double[2 * nfreqs]; // (double *) malloc0 (2 * nfreqs * sizeof (double));
|
||||
double gpreamp, f, frac;
|
||||
double* 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 (double), 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++;
|
||||
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++;
|
||||
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-100) lowmag = 1.0e-100;
|
||||
A[k] = lowmag;
|
||||
}
|
||||
k = high;
|
||||
while (++k <= mid)
|
||||
{
|
||||
f = (double)k / (double)mid;
|
||||
highmag *= fhigh4 / (f * f * f * f);
|
||||
if (highmag < 1.0e-100) highmag = 1.0e-100;
|
||||
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-100) lowmag = 1.0e-100;
|
||||
A[k] = lowmag;
|
||||
}
|
||||
k = high;
|
||||
while (++k < mid)
|
||||
{
|
||||
f = (double)k / (double)mid;
|
||||
highmag *= fhigh4 / (f * f * f * f);
|
||||
if (highmag < 1.0e-100) highmag = 1.0e-100;
|
||||
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,
|
||||
double *in,
|
||||
double *out,
|
||||
int nfreqs,
|
||||
double* F,
|
||||
double* G,
|
||||
int ctfmode,
|
||||
int wintype,
|
||||
int samplerate
|
||||
)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
EQP *a = new EQP;
|
||||
double* 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 double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
|
||||
a->ctfmode = ctfmode;
|
||||
a->wintype = wintype;
|
||||
a->samplerate = (double)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
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EQP::setBuffers_eqp (EQP *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void EQP::setSamplerate_eqp (EQP *a, int rate)
|
||||
{
|
||||
double* 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)
|
||||
{
|
||||
double* 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.csDSP.lock();
|
||||
rxa.eqp.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EQP::SetEQNC (RXA& rxa, int nc)
|
||||
{
|
||||
EQP *a;
|
||||
double* impulse;
|
||||
rxa.csDSP.lock();
|
||||
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);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
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, double* F, double* G)
|
||||
{
|
||||
EQP *a;
|
||||
double* impulse;
|
||||
a = rxa.eqp.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = nfreqs;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
|
||||
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;
|
||||
double* 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;
|
||||
double* 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;
|
||||
double* impulse;
|
||||
a = rxa.eqp.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = 4;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->F[1] = 150.0;
|
||||
a->F[2] = 400.0;
|
||||
a->F[3] = 1500.0;
|
||||
a->F[4] = 6000.0;
|
||||
a->G[0] = (double)rxeq[0];
|
||||
a->G[1] = (double)rxeq[1];
|
||||
a->G[2] = (double)rxeq[1];
|
||||
a->G[3] = (double)rxeq[2];
|
||||
a->G[4] = (double)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;
|
||||
double* impulse;
|
||||
int i;
|
||||
a = rxa.eqp.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = 10;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
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] = (double)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.csDSP.lock();
|
||||
txa.eqp.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void EQP::SetEQNC (TXA& txa, int nc)
|
||||
{
|
||||
EQP *a;
|
||||
double* impulse;
|
||||
txa.csDSP.lock();
|
||||
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);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
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, double* F, double* G)
|
||||
{
|
||||
EQP *a;
|
||||
double* impulse;
|
||||
a = txa.eqp.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = nfreqs;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
|
||||
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;
|
||||
double* 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;
|
||||
double* 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;
|
||||
double* impulse;
|
||||
a = txa.eqp.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = 4;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->F[1] = 150.0;
|
||||
a->F[2] = 400.0;
|
||||
a->F[3] = 1500.0;
|
||||
a->F[4] = 6000.0;
|
||||
a->G[0] = (double)txeq[0];
|
||||
a->G[1] = (double)txeq[1];
|
||||
a->G[2] = (double)txeq[1];
|
||||
a->G[3] = (double)txeq[2];
|
||||
a->G[4] = (double)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;
|
||||
double* impulse;
|
||||
int i;
|
||||
a = txa.eqp.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = 10;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
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] = (double)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 *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
double* EQP::eq_mults (int size, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype)
|
||||
{
|
||||
double* impulse = eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype);
|
||||
double* mults = FIR::fftcv_mults(2 * size, impulse);
|
||||
delete[] (impulse);
|
||||
return mults;
|
||||
}
|
||||
|
||||
void EQ::calc_eq (EQ *a)
|
||||
{
|
||||
a->scale = 1.0 / (double)(2 * a->size);
|
||||
a->infilt = new double[2 * a->size * 2]; // (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->product = new double[2 * a->size * 2]; // (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->CFor = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->infilt, (fftw_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->CRev = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->product, (fftw_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);
|
||||
}
|
||||
|
||||
void EQ::decalc_eq (EQ *a)
|
||||
{
|
||||
fftw_destroy_plan(a->CRev);
|
||||
fftw_destroy_plan(a->CFor);
|
||||
delete[] (a->mults);
|
||||
delete[] (a->product);
|
||||
delete[] (a->infilt);
|
||||
}
|
||||
|
||||
EQ* EQ::create_eq (int run, int size, double *in, double *out, int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate)
|
||||
{
|
||||
EQ *a = new EQ;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->nfreqs = nfreqs;
|
||||
a->F = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = new double[a->nfreqs + 1]; // (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
|
||||
a->ctfmode = ctfmode;
|
||||
a->wintype = wintype;
|
||||
a->samplerate = (double)samplerate;
|
||||
calc_eq (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void EQ::destroy_eq (EQ *a)
|
||||
{
|
||||
decalc_eq (a);
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
delete[] (a);
|
||||
}
|
||||
|
||||
void EQ::flush_eq (EQ *a)
|
||||
{
|
||||
memset (a->infilt, 0, 2 * a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EQ::xeq (EQ *a)
|
||||
{
|
||||
int i;
|
||||
double I, Q;
|
||||
if (a->run)
|
||||
{
|
||||
memcpy (&(a->infilt[2 * a->size]), a->in, a->size * sizeof (dcomplex));
|
||||
fftw_execute (a->CFor);
|
||||
for (i = 0; i < 2 * a->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];
|
||||
}
|
||||
fftw_execute (a->CRev);
|
||||
memcpy (a->infilt, &(a->infilt[2 * a->size]), a->size * sizeof(dcomplex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void EQ::setBuffers_eq (EQ *a, double* in, double* out)
|
||||
{
|
||||
decalc_eq (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_eq (a);
|
||||
}
|
||||
|
||||
void EQ::setSamplerate_eq (EQ *a, int rate)
|
||||
{
|
||||
decalc_eq (a);
|
||||
a->samplerate = rate;
|
||||
calc_eq (a);
|
||||
}
|
||||
|
||||
void EQ::setSize_eq (EQ *a, int size)
|
||||
{
|
||||
decalc_eq (a);
|
||||
a->size = size;
|
||||
calc_eq (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* 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, double* F, double* G)
|
||||
{
|
||||
EQ a;
|
||||
ch.csDSP.lock();
|
||||
a = rxa.eq.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = nfreqs;
|
||||
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
|
||||
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 = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->F[1] = 150.0;
|
||||
a->F[2] = 400.0;
|
||||
a->F[3] = 1500.0;
|
||||
a->F[4] = 6000.0;
|
||||
a->G[0] = (double)rxeq[0];
|
||||
a->G[1] = (double)rxeq[1];
|
||||
a->G[2] = (double)rxeq[1];
|
||||
a->G[3] = (double)rxeq[2];
|
||||
a->G[4] = (double)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 = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
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] = (double)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, double* F, double* G)
|
||||
{
|
||||
EQ a;
|
||||
ch.csDSP.lock();
|
||||
a = txa.eq.p;
|
||||
delete[] (a->G);
|
||||
delete[] (a->F);
|
||||
a->nfreqs = nfreqs;
|
||||
a->F = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->F, F, (nfreqs + 1) * sizeof (double));
|
||||
memcpy (a->G, G, (nfreqs + 1) * sizeof (double));
|
||||
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 = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->F[1] = 150.0;
|
||||
a->F[2] = 400.0;
|
||||
a->F[3] = 1500.0;
|
||||
a->F[4] = 6000.0;
|
||||
a->G[0] = (double)txeq[0];
|
||||
a->G[1] = (double)txeq[1];
|
||||
a->G[2] = (double)txeq[1];
|
||||
a->G[3] = (double)txeq[2];
|
||||
a->G[4] = (double)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 = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
a->G = (double *) malloc0 ((a->nfreqs + 1) * sizeof (double));
|
||||
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] = (double)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
|
||||
164
wdsp/eq.hpp
Normal file
164
wdsp/eq.hpp
Normal file
@ -0,0 +1,164 @@
|
||||
/* 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 "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class RXA;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API EQP
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
int nc;
|
||||
int mp;
|
||||
double* in;
|
||||
double* out;
|
||||
int nfreqs;
|
||||
double* F;
|
||||
double* G;
|
||||
int ctfmode;
|
||||
int wintype;
|
||||
double samplerate;
|
||||
FIRCORE *p;
|
||||
|
||||
static EQP* create_eqp (
|
||||
int run,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double *in,
|
||||
double *out,
|
||||
int nfreqs,
|
||||
double* F,
|
||||
double* G,
|
||||
int ctfmode,
|
||||
int wintype,
|
||||
int samplerate
|
||||
);
|
||||
static double* eq_impulse (int N, int nfreqs, double* F, double* 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, double* in, double* 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, double* F, double* 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, double* F, double* 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 double* eq_mults (int size, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype);
|
||||
|
||||
private:
|
||||
static int fEQcompare (const void * a, const void * b);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Equalizer *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_eq_h
|
||||
#define wdsp_eq_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API EQ
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
int nfreqs;
|
||||
double* F;
|
||||
double* G;
|
||||
double* infilt;
|
||||
double* product;
|
||||
double* mults;
|
||||
double scale;
|
||||
int ctfmode;
|
||||
int wintype;
|
||||
double samplerate;
|
||||
fftw_plan CFor;
|
||||
fftw_plan CRev;
|
||||
|
||||
static EQ* create_eq (int run, int size, double *in, double *out, int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate);
|
||||
// static double* eq_mults (int size, int nfreqs, double* F, double* G, double samplerate, double 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, double* in, double* out);
|
||||
static void setSamplerate_eq (EQ *a, int rate);
|
||||
static void setSize_eq (EQ *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_eq (EQ *a);
|
||||
static void decalc_eq (EQ *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
161
wdsp/fcurve.cpp
Normal file
161
wdsp/fcurve.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/* fcurve.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "fcurve.hpp"
|
||||
#include "fir.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
double* FCurve::fc_impulse (int nc, double f0, double f1, double g0, double, int curve, double samplerate, double scale, int ctfmode, int wintype)
|
||||
{
|
||||
double* A = new double[nc / 2 + 1]; // (double *) malloc0 ((nc / 2 + 1) * sizeof (double));
|
||||
int i;
|
||||
double fn, f;
|
||||
double* impulse;
|
||||
int mid = nc / 2;
|
||||
double g0_lin = pow(10.0, g0 / 20.0);
|
||||
if (nc & 1)
|
||||
{
|
||||
for (i = 0; i <= mid; i++)
|
||||
{
|
||||
fn = (double)i / (double)mid;
|
||||
f = fn * samplerate / 2.0;
|
||||
switch (curve)
|
||||
{
|
||||
case 0: // fm pre-emphasis
|
||||
if (f0 > 0.0)
|
||||
A[i] = scale * (g0_lin * f / f0);
|
||||
else
|
||||
A[i] = 0.0;
|
||||
break;
|
||||
case 1: // fm de-emphasis
|
||||
if (f > 0.0)
|
||||
A[i] = scale * (g0_lin * f0 / f);
|
||||
else
|
||||
A[i] = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < mid; i++)
|
||||
{
|
||||
fn = ((double)i + 0.5) / (double)mid;
|
||||
f = fn * samplerate / 2.0;
|
||||
switch (curve)
|
||||
{
|
||||
case 0: // fm pre-emphasis
|
||||
if (f0 > 0.0)
|
||||
A[i] = scale * (g0_lin * f / f0);
|
||||
else
|
||||
A[i] = 0.0;
|
||||
break;
|
||||
case 1: // fm de-emphasis
|
||||
if (f > 0.0)
|
||||
A[i] = scale * (g0_lin * f0 / f);
|
||||
else
|
||||
A[i] = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctfmode == 0)
|
||||
{
|
||||
int k, low, high;
|
||||
double lowmag, highmag, flow4, fhigh4;
|
||||
if (nc & 1)
|
||||
{
|
||||
low = (int)(2.0 * f0 / samplerate * mid);
|
||||
high = (int)(2.0 * f1 / samplerate * 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-100) lowmag = 1.0e-100;
|
||||
A[k] = lowmag;
|
||||
}
|
||||
k = high;
|
||||
while (++k <= mid)
|
||||
{
|
||||
f = (double)k / (double)mid;
|
||||
highmag *= fhigh4 / (f * f * f * f);
|
||||
if (highmag < 1.0e-100) highmag = 1.0e-100;
|
||||
A[k] = highmag;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
low = (int)(2.0 * f0 / samplerate * mid - 0.5);
|
||||
high = (int)(2.0 * f1 / samplerate * 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-100) lowmag = 1.0e-100;
|
||||
A[k] = lowmag;
|
||||
}
|
||||
k = high;
|
||||
while (++k < mid)
|
||||
{
|
||||
f = (double)k / (double)mid;
|
||||
highmag *= fhigh4 / (f * f * f * f);
|
||||
if (highmag < 1.0e-100) highmag = 1.0e-100;
|
||||
A[k] = highmag;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nc & 1)
|
||||
impulse = FIR::fir_fsamp_odd(nc, A, 1, 1.0, wintype);
|
||||
else
|
||||
impulse = FIR::fir_fsamp(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
|
||||
double* FCurve::fc_mults (int size, double f0, double f1, double g0, double g1, int curve, double samplerate, double scale, int ctfmode, int wintype)
|
||||
{
|
||||
double* impulse = fc_impulse (size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype);
|
||||
double* mults = FIR::fftcv_mults(2 * size, impulse);
|
||||
delete[] (impulse);
|
||||
return mults;
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
44
wdsp/fcurve.hpp
Normal file
44
wdsp/fcurve.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
/* fcurve.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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_fcurve_h
|
||||
#define wdsp_fcurve_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API FCurve
|
||||
{
|
||||
public:
|
||||
static double* fc_impulse (int nc, double f0, double f1, double g0, double g1, int curve, double samplerate, double scale, int ctfmode, int wintype);
|
||||
static double* fc_mults (int size, double f0, double f1, double g0, double g1, int curve, double samplerate, double scale, int ctfmode, int wintype);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
415
wdsp/fir.cpp
Normal file
415
wdsp/fir.cpp
Normal file
@ -0,0 +1,415 @@
|
||||
/* fir.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2022 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@pratt.one
|
||||
*/
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include "fftw3.h"
|
||||
#include "comm.hpp"
|
||||
#include "fir.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
double* FIR::fftcv_mults (int NM, double* c_impulse)
|
||||
{
|
||||
double* mults = new double[NM * 2]; // (double *) malloc0 (NM * sizeof (dcomplex));
|
||||
double* cfft_impulse = new double[NM * 2]; // (double *) malloc0 (NM * sizeof (dcomplex));
|
||||
fftw_plan ptmp = fftw_plan_dft_1d(NM, (fftw_complex *) cfft_impulse,
|
||||
(fftw_complex *) mults, FFTW_FORWARD, FFTW_PATIENT);
|
||||
memset (cfft_impulse, 0, NM * sizeof (dcomplex));
|
||||
// store complex coefs right-justified in the buffer
|
||||
memcpy (&(cfft_impulse[NM - 2]), c_impulse, (NM / 2 + 1) * sizeof(dcomplex));
|
||||
fftw_execute (ptmp);
|
||||
fftw_destroy_plan (ptmp);
|
||||
delete[] cfft_impulse;
|
||||
return mults;
|
||||
}
|
||||
|
||||
double* FIR::get_fsamp_window(int N, int wintype)
|
||||
{
|
||||
int i;
|
||||
double arg0, arg1;
|
||||
double* window = new double[N]; // (double *) malloc0 (N * sizeof(double));
|
||||
switch (wintype)
|
||||
{
|
||||
case 0:
|
||||
arg0 = 2.0 * PI / ((double)N - 1.0);
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
arg1 = cos(arg0 * (double)i);
|
||||
window[i] = +0.21747
|
||||
+ arg1 * (-0.45325
|
||||
+ arg1 * (+0.28256
|
||||
+ arg1 * (-0.04672)));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
arg0 = 2.0 * PI / ((double)N - 1.0);
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
arg1 = cos(arg0 * (double)i);
|
||||
window[i] = +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))))));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (i = 0; i < N; i++)
|
||||
window[i] = 1.0;
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
double* FIR::fir_fsamp_odd (int N, double* A, int rtype, double scale, int wintype)
|
||||
{
|
||||
int i, j;
|
||||
int mid = (N - 1) / 2;
|
||||
double mag, phs;
|
||||
double* window;
|
||||
double *fcoef = new double[N * 2]; // (double *) malloc0 (N * sizeof (dcomplex));
|
||||
double *c_impulse = new double[N * 2]; // (double *) malloc0 (N * sizeof (dcomplex));
|
||||
fftw_plan ptmp = fftw_plan_dft_1d(N, (fftw_complex *)fcoef, (fftw_complex *)c_impulse, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
double local_scale = 1.0 / (double)N;
|
||||
for (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);
|
||||
}
|
||||
for (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];
|
||||
}
|
||||
fftw_execute (ptmp);
|
||||
fftw_destroy_plan (ptmp);
|
||||
delete[] fcoef;
|
||||
window = get_fsamp_window(N, wintype);
|
||||
switch (rtype)
|
||||
{
|
||||
case 0:
|
||||
for (i = 0; i < N; i++)
|
||||
c_impulse[i] = scale * c_impulse[2 * i] * window[i];
|
||||
break;
|
||||
case 1:
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
c_impulse[2 * i + 0] *= scale * window[i];
|
||||
c_impulse[2 * i + 1] = 0.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
delete[] window;
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
double* FIR::fir_fsamp (int N, double* A, int rtype, double scale, int wintype)
|
||||
{
|
||||
int n, i, j, k;
|
||||
double sum;
|
||||
double* window;
|
||||
double *c_impulse = new double[N * 2]; // (double *) malloc0 (N * sizeof (complex));
|
||||
|
||||
if (N & 1)
|
||||
{
|
||||
int M = (N - 1) / 2;
|
||||
for (n = 0; n < M + 1; n++)
|
||||
{
|
||||
sum = 0.0;
|
||||
for (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 + 1] = 0.0;
|
||||
}
|
||||
for (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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double M = (double)(N - 1) / 2.0;
|
||||
for (n = 0; n < N / 2; n++)
|
||||
{
|
||||
sum = 0.0;
|
||||
for (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 + 1] = 0.0;
|
||||
}
|
||||
for (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);
|
||||
switch (rtype)
|
||||
{
|
||||
case 0:
|
||||
for (i = 0; i < N; i++)
|
||||
c_impulse[i] = scale * c_impulse[2 * i] * window[i];
|
||||
break;
|
||||
case 1:
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
c_impulse[2 * i + 0] *= scale * window[i];
|
||||
c_impulse[2 * i + 1] = 0.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
delete[] window;
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
double* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale)
|
||||
{
|
||||
double *c_impulse = new double[N * 2]; // (double *) malloc0 (N * sizeof (complex));
|
||||
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;
|
||||
|
||||
if (N & 1)
|
||||
{
|
||||
switch (rtype)
|
||||
{
|
||||
case 0:
|
||||
c_impulse[N >> 1] = scale * 2.0 * ft;
|
||||
break;
|
||||
case 1:
|
||||
c_impulse[N - 1] = scale * 2.0 * ft;
|
||||
c_impulse[ N ] = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (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)
|
||||
{
|
||||
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
|
||||
+ cosphi * ( + 3.5015956323820469e-01
|
||||
+ cosphi * ( - 2.4774111897080783e-01
|
||||
+ cosphi * ( + 8.5438256055858031e-02
|
||||
+ cosphi * ( - 1.2320203369293225e-02
|
||||
+ cosphi * ( + 4.3778825791773474e-04 ))))));
|
||||
break;
|
||||
}
|
||||
coef = scale * sinc * window;
|
||||
switch (rtype)
|
||||
{
|
||||
case 0:
|
||||
c_impulse[i] = + coef * cos (posi * w_osc);
|
||||
c_impulse[j] = + 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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
double *FIR::fir_read (int N, const char *filename, int rtype, double scale)
|
||||
// N = number of real or complex coefficients (see rtype)
|
||||
// *filename = filename
|
||||
// rtype = 0: real coefficients
|
||||
// rtype = 1: complex coefficients
|
||||
// scale = a scale factor that will be applied to the returned coefficients;
|
||||
// if this is not needed, set it to 1.0
|
||||
// NOTE: The number of values in the file must NOT exceed those implied by N and rtype
|
||||
{
|
||||
FILE *file;
|
||||
int i;
|
||||
double I, Q;
|
||||
double *c_impulse = new double[N * 2]; // (double *) malloc0 (N * sizeof (complex));
|
||||
file = fopen (filename, "r");
|
||||
for (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.
|
||||
switch (rtype)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
int r = fscanf (file, "%le", &I);
|
||||
fprintf(stderr, "^%d parameters read\n", r);
|
||||
c_impulse[i] = + scale * I;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
int r = fscanf (file, "%le", &I);
|
||||
fprintf(stderr, "%d parameters read\n", r);
|
||||
r = fscanf (file, "%le", &Q);
|
||||
fprintf(stderr, "%d parameters read\n", r);
|
||||
c_impulse[2 * i + 0] = + scale * I;
|
||||
c_impulse[2 * i + 1] = - scale * Q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose (file);
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
void FIR::analytic (int N, double* in, double* out)
|
||||
{
|
||||
int i;
|
||||
double inv_N = 1.0 / (double)N;
|
||||
double two_inv_N = 2.0 * inv_N;
|
||||
double* x = new double[N * 2]; // (double *) malloc0 (N * sizeof (complex));
|
||||
fftw_plan pfor = fftw_plan_dft_1d (N, (fftw_complex *) in,
|
||||
(fftw_complex *) x, FFTW_FORWARD, FFTW_PATIENT);
|
||||
fftw_plan prev = fftw_plan_dft_1d (N, (fftw_complex *) x,
|
||||
(fftw_complex *) out, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
fftw_execute (pfor);
|
||||
x[0] *= inv_N;
|
||||
x[1] *= inv_N;
|
||||
for (i = 1; i < N / 2; i++)
|
||||
{
|
||||
x[2 * i + 0] *= two_inv_N;
|
||||
x[2 * i + 1] *= two_inv_N;
|
||||
}
|
||||
x[N + 0] *= inv_N;
|
||||
x[N + 1] *= inv_N;
|
||||
memset (&x[N + 2], 0, (N - 2) * sizeof (double));
|
||||
fftw_execute (prev);
|
||||
fftw_destroy_plan (prev);
|
||||
fftw_destroy_plan (pfor);
|
||||
delete[] x;
|
||||
}
|
||||
|
||||
void FIR::mp_imp (int N, double* fir, double* mpfir, int pfactor, int polarity)
|
||||
{
|
||||
int i;
|
||||
int size = N * pfactor;
|
||||
double inv_PN = 1.0 / (double)size;
|
||||
double* firpad = new double[size * 2]; // (double *) malloc0 (size * sizeof (complex));
|
||||
double* firfreq = new double[size * 2]; // (double *) malloc0 (size * sizeof (complex));
|
||||
double* mag = new double[size]; // (double *) malloc0 (size * sizeof (double));
|
||||
double* ana = new double[size * 2]; // (double *) malloc0 (size * sizeof (complex));
|
||||
double* impulse = new double[size * 2]; // (double *) malloc0 (size * sizeof (complex));
|
||||
double* newfreq = new double[size * 2]; // (double *) malloc0 (size * sizeof (complex));
|
||||
memcpy (firpad, fir, N * sizeof (dcomplex));
|
||||
fftw_plan pfor = fftw_plan_dft_1d (size, (fftw_complex *) firpad,
|
||||
(fftw_complex *) firfreq, FFTW_FORWARD, FFTW_PATIENT);
|
||||
fftw_plan prev = fftw_plan_dft_1d (size, (fftw_complex *) newfreq,
|
||||
(fftw_complex *) impulse, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
// print_impulse("orig_imp.txt", N, fir, 1, 0);
|
||||
fftw_execute (pfor);
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
mag[i] = sqrt (firfreq[2 * i + 0] * firfreq[2 * i + 0] + firfreq[2 * i + 1] * firfreq[2 * i + 1]) * inv_PN;
|
||||
if (mag[i] > 0.0)
|
||||
ana[2 * i + 0] = log (mag[i]);
|
||||
else
|
||||
ana[2 * i + 0] = log (1.0e-300);
|
||||
}
|
||||
analytic (size, ana, ana);
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
newfreq[2 * i + 0] = + mag[i] * cos (ana[2 * i + 1]);
|
||||
if (polarity)
|
||||
newfreq[2 * i + 1] = + mag[i] * sin (ana[2 * i + 1]);
|
||||
else
|
||||
newfreq[2 * i + 1] = - mag[i] * sin (ana[2 * i + 1]);
|
||||
}
|
||||
fftw_execute (prev);
|
||||
if (polarity)
|
||||
memcpy (mpfir, &impulse[2 * (pfactor - 1) * N], N * sizeof (dcomplex));
|
||||
else
|
||||
memcpy (mpfir, impulse, N * sizeof (dcomplex));
|
||||
// print_impulse("min_imp.txt", N, mpfir, 1, 0);
|
||||
fftw_destroy_plan (prev);
|
||||
fftw_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
|
||||
double* FIR::zff_impulse(int nc, double 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
|
||||
double* resdet = new double[n_resdet]; // (double*)malloc0 (n_resdet * sizeof(double));
|
||||
for (int i = 1, j = 0, k = n_resdet - 1; i < nc / 4; i++, j++, k--)
|
||||
resdet[j] = resdet[k] = (double)(i * (i + 1) / 2);
|
||||
resdet[nc / 4 - 1] = (double)(nc / 4 * (nc / 4 + 1) / 2);
|
||||
// print_impulse ("resdet", n_resdet, resdet, 0, 0);
|
||||
// allocate the double and complex versions and make the values
|
||||
double* dresdet = new double[n_dresdet]; // (double*)malloc0 (n_dresdet * sizeof(double));
|
||||
double div = (double)((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor
|
||||
double* c_dresdet = new double[nc * 2]; // (double*)malloc0 (nc * sizeof(complex));
|
||||
for (int n = 0; n < n_dresdet; n++) // convolve to make the cascade
|
||||
{
|
||||
for (int k = 0; k < n_resdet; k++)
|
||||
if ((n - k) >= 0 && (n - k) < n_resdet)
|
||||
dresdet[n] += resdet[k] * resdet[n - k];
|
||||
dresdet[n] /= div;
|
||||
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
|
||||
52
wdsp/fir.hpp
Normal file
52
wdsp/fir.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
/* fir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2022 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@pratt.one
|
||||
|
||||
*/
|
||||
#ifndef wdsp_fir_h
|
||||
#define wdsp_fir_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API FIR
|
||||
{
|
||||
public:
|
||||
static double* fftcv_mults (int NM, double* c_impulse);
|
||||
static double* fir_fsamp_odd (int N, double* A, int rtype, double scale, int wintype);
|
||||
static double* fir_fsamp (int N, double* A, int rtype, double scale, int wintype);
|
||||
static double* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale);
|
||||
static double* get_fsamp_window(int N, int wintype);
|
||||
static double *fir_read (int N, const char *filename, int rtype, double scale);
|
||||
static void mp_imp (int N, double* fir, double* mpfir, int pfactor, int polarity);
|
||||
static double* zff_impulse(int nc, double scale);
|
||||
|
||||
private:
|
||||
static void analytic (int N, double* in, double* out);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace WDSP
|
||||
490
wdsp/firmin.cpp
Normal file
490
wdsp/firmin.cpp
Normal file
@ -0,0 +1,490 @@
|
||||
/* firmin.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 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
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "firmin.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Time-Domain FIR *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void FIRMIN::calc_firmin (FIRMIN *a)
|
||||
{
|
||||
a->h = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain);
|
||||
a->rsize = a->nc;
|
||||
a->mask = a->rsize - 1;
|
||||
a->ring = new double[a->rsize * 2]; // (double *) malloc0 (a->rsize * sizeof (complex));
|
||||
a->idx = 0;
|
||||
}
|
||||
|
||||
FIRMIN* FIRMIN::create_firmin (int run, int position, int size, double* in, double* out,
|
||||
int nc, double f_low, double f_high, int samplerate, int wintype, double gain)
|
||||
{
|
||||
FIRMIN *a = new FIRMIN;
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->nc = nc;
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
a->samplerate = samplerate;
|
||||
a->wintype = wintype;
|
||||
a->gain = gain;
|
||||
calc_firmin (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void FIRMIN::destroy_firmin (FIRMIN *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete[] (a->h);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FIRMIN::flush_firmin (FIRMIN *a)
|
||||
{
|
||||
memset (a->ring, 0, a->rsize * sizeof (dcomplex));
|
||||
a->idx = 0;
|
||||
}
|
||||
|
||||
void FIRMIN::xfirmin (FIRMIN *a, int pos)
|
||||
{
|
||||
if (a->run && a->position == pos)
|
||||
{
|
||||
int i, j, k;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->ring[2 * a->idx + 0] = a->in[2 * i + 0];
|
||||
a->ring[2 * a->idx + 1] = a->in[2 * i + 1];
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
k = a->idx;
|
||||
for (j = 0; j < a->nc; j++)
|
||||
{
|
||||
a->out[2 * i + 0] += a->h[2 * j + 0] * a->ring[2 * k + 0] - a->h[2 * j + 1] * a->ring[2 * k + 1];
|
||||
a->out[2 * i + 1] += a->h[2 * j + 0] * a->ring[2 * k + 1] + a->h[2 * j + 1] * a->ring[2 * k + 0];
|
||||
k = (k + a->mask) & a->mask;
|
||||
}
|
||||
a->idx = (a->idx + 1) & a->mask;
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void FIRMIN::setBuffers_firmin (FIRMIN *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void FIRMIN::setSamplerate_firmin (FIRMIN *a, int rate)
|
||||
{
|
||||
a->samplerate = (double)rate;
|
||||
calc_firmin (a);
|
||||
}
|
||||
|
||||
void FIRMIN::setSize_firmin (FIRMIN *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
void FIRMIN::setFreqs_firmin (FIRMIN *a, double f_low, double f_high)
|
||||
{
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
calc_firmin (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Standalone Partitioned Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void FIROPT::plan_firopt (FIROPT *a)
|
||||
{
|
||||
// must call for change in 'nc', 'size', 'out'
|
||||
int i;
|
||||
a->nfor = a->nc / a->size;
|
||||
a->buffidx = 0;
|
||||
a->idxmask = a->nfor - 1;
|
||||
a->fftin = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fftout = new double*[a->nfor]; // (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->fmask = new double*[a->nfor]; // (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->maskgen = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->pcfor = new fftw_plan[a->nfor]; // (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
a->maskplan = new fftw_plan[a->nfor]; // (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
a->fftout[i] = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fmask[i] = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->pcfor[i] = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->fftin, (fftw_complex *)a->fftout[i], FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->maskplan[i] = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->maskgen, (fftw_complex *)a->fmask[i], FFTW_FORWARD, FFTW_PATIENT);
|
||||
}
|
||||
a->accum = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->crev = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->accum, (fftw_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
}
|
||||
|
||||
void FIROPT::calc_firopt (FIROPT *a)
|
||||
{
|
||||
// call for change in frequency, rate, wintype, gain
|
||||
// must also call after a call to plan_firopt()
|
||||
int i;
|
||||
double* impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain);
|
||||
a->buffidx = 0;
|
||||
for (i = 0; i < a->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.
|
||||
memcpy (&(a->maskgen[2 * a->size]), &(impulse[2 * a->size * i]), a->size * sizeof(dcomplex));
|
||||
fftw_execute (a->maskplan[i]);
|
||||
}
|
||||
delete[] (impulse);
|
||||
}
|
||||
|
||||
FIROPT* FIROPT::create_firopt (int run, int position, int size, double* in, double* out,
|
||||
int nc, double f_low, double f_high, int samplerate, int wintype, double gain)
|
||||
{
|
||||
FIROPT *a = new FIROPT;
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->nc = nc;
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
a->samplerate = samplerate;
|
||||
a->wintype = wintype;
|
||||
a->gain = gain;
|
||||
plan_firopt (a);
|
||||
calc_firopt (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void FIROPT::deplan_firopt (FIROPT *a)
|
||||
{
|
||||
int i;
|
||||
fftw_destroy_plan (a->crev);
|
||||
delete[] (a->accum);
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
delete[] (a->fftout[i]);
|
||||
delete[] (a->fmask[i]);
|
||||
fftw_destroy_plan (a->pcfor[i]);
|
||||
fftw_destroy_plan (a->maskplan[i]);
|
||||
}
|
||||
delete[] (a->maskplan);
|
||||
delete[] (a->pcfor);
|
||||
delete[] (a->maskgen);
|
||||
delete[] (a->fmask);
|
||||
delete[] (a->fftout);
|
||||
delete[] (a->fftin);
|
||||
}
|
||||
|
||||
void FIROPT::destroy_firopt (FIROPT *a)
|
||||
{
|
||||
deplan_firopt (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FIROPT::flush_firopt (FIROPT *a)
|
||||
{
|
||||
int i;
|
||||
memset (a->fftin, 0, 2 * a->size * sizeof (dcomplex));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
memset (a->fftout[i], 0, 2 * a->size * sizeof (dcomplex));
|
||||
a->buffidx = 0;
|
||||
}
|
||||
|
||||
void FIROPT::xfiropt (FIROPT *a, int pos)
|
||||
{
|
||||
if (a->run && (a->position == pos))
|
||||
{
|
||||
int i, j, k;
|
||||
memcpy (&(a->fftin[2 * a->size]), a->in, a->size * sizeof (dcomplex));
|
||||
fftw_execute (a->pcfor[a->buffidx]);
|
||||
k = a->buffidx;
|
||||
memset (a->accum, 0, 2 * a->size * sizeof (dcomplex));
|
||||
for (j = 0; j < a->nfor; j++)
|
||||
{
|
||||
for (i = 0; i < 2 * a->size; i++)
|
||||
{
|
||||
a->accum[2 * i + 0] += a->fftout[k][2 * i + 0] * a->fmask[j][2 * i + 0] - a->fftout[k][2 * i + 1] * a->fmask[j][2 * i + 1];
|
||||
a->accum[2 * i + 1] += a->fftout[k][2 * i + 0] * a->fmask[j][2 * i + 1] + a->fftout[k][2 * i + 1] * a->fmask[j][2 * i + 0];
|
||||
}
|
||||
k = (k + a->idxmask) & a->idxmask;
|
||||
}
|
||||
a->buffidx = (a->buffidx + 1) & a->idxmask;
|
||||
fftw_execute (a->crev);
|
||||
memcpy (a->fftin, &(a->fftin[2 * a->size]), a->size * sizeof(dcomplex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void FIROPT::setBuffers_firopt (FIROPT *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
deplan_firopt (a);
|
||||
plan_firopt (a);
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
void FIROPT::setSamplerate_firopt (FIROPT *a, int rate)
|
||||
{
|
||||
a->samplerate = rate;
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
void FIROPT::setSize_firopt (FIROPT *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
deplan_firopt (a);
|
||||
plan_firopt (a);
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
void FIROPT::setFreqs_firopt (FIROPT *a, double f_low, double f_high)
|
||||
{
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Filter Kernel *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
void FIRCORE::plan_fircore (FIRCORE *a)
|
||||
{
|
||||
// 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 double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fftout = new double*[a->nfor]; // (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->fmask = new double**[2]; // (double ***) malloc0 (2 * sizeof (double **));
|
||||
a->fmask[0] = new double*[a->nfor]; // (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->fmask[1] = new double*[a->nfor]; // (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->maskgen = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->pcfor = new fftw_plan[a->nfor]; // (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
a->maskplan = new fftw_plan*[2]; // (fftw_plan **) malloc0 (2 * sizeof (fftw_plan *));
|
||||
a->maskplan[0] = new fftw_plan[a->nfor]; // (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
a->maskplan[1] = new fftw_plan[a->nfor]; // (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
a->fftout[i] = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fmask[0][i] = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fmask[1][i] = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->pcfor[i] = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->fftin, (fftw_complex *)a->fftout[i], FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->maskplan[0][i] = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->maskgen, (fftw_complex *)a->fmask[0][i], FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->maskplan[1][i] = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->maskgen, (fftw_complex *)a->fmask[1][i], FFTW_FORWARD, FFTW_PATIENT);
|
||||
}
|
||||
a->accum = new double[2 * a->size * 2]; // (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->crev = fftw_plan_dft_1d(2 * a->size, (fftw_complex *)a->accum, (fftw_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
a->masks_ready = 0;
|
||||
}
|
||||
|
||||
void FIRCORE::calc_fircore (FIRCORE *a, 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);
|
||||
else
|
||||
memcpy (a->imp, a->impulse, a->nc * sizeof (dcomplex));
|
||||
for (i = 0; i < a->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.
|
||||
memcpy (&(a->maskgen[2 * a->size]), &(a->imp[2 * a->size * i]), a->size * sizeof(dcomplex));
|
||||
fftw_execute (a->maskplan[1 - a->cset][i]);
|
||||
}
|
||||
a->masks_ready = 1;
|
||||
if (flip)
|
||||
{
|
||||
a->update.lock();
|
||||
a->cset = 1 - a->cset;
|
||||
a->update.unlock();
|
||||
a->masks_ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FIRCORE* FIRCORE::create_fircore (int size, double* in, double* out, int nc, int mp, double* 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 double[a->nc * 2]; // (double *) malloc0 (a->nc * sizeof (complex));
|
||||
a->imp = new double[a->nc * 2]; // (double *) malloc0 (a->nc * sizeof (complex));
|
||||
memcpy (a->impulse, impulse, a->nc * sizeof (dcomplex));
|
||||
calc_fircore (a, 1);
|
||||
return a;
|
||||
}
|
||||
|
||||
void FIRCORE::deplan_fircore (FIRCORE *a)
|
||||
{
|
||||
int i;
|
||||
fftw_destroy_plan (a->crev);
|
||||
delete[] (a->accum);
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
delete[] (a->fftout[i]);
|
||||
delete[] (a->fmask[0][i]);
|
||||
delete[] (a->fmask[1][i]);
|
||||
fftw_destroy_plan (a->pcfor[i]);
|
||||
fftw_destroy_plan (a->maskplan[0][i]);
|
||||
fftw_destroy_plan (a->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)
|
||||
{
|
||||
deplan_fircore (a);
|
||||
delete[] (a->imp);
|
||||
delete[] (a->impulse);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FIRCORE::flush_fircore (FIRCORE *a)
|
||||
{
|
||||
int i;
|
||||
memset (a->fftin, 0, 2 * a->size * sizeof (dcomplex));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
memset (a->fftout[i], 0, 2 * a->size * sizeof (dcomplex));
|
||||
a->buffidx = 0;
|
||||
}
|
||||
|
||||
void FIRCORE::xfircore (FIRCORE *a)
|
||||
{
|
||||
int i, j, k;
|
||||
memcpy (&(a->fftin[2 * a->size]), a->in, a->size * sizeof (dcomplex));
|
||||
fftw_execute (a->pcfor[a->buffidx]);
|
||||
k = a->buffidx;
|
||||
memset (a->accum, 0, 2 * a->size * sizeof (dcomplex));
|
||||
a->update.lock();
|
||||
for (j = 0; j < a->nfor; j++)
|
||||
{
|
||||
for (i = 0; i < 2 * a->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];
|
||||
}
|
||||
k = (k + a->idxmask) & a->idxmask;
|
||||
}
|
||||
a->update.unlock();
|
||||
a->buffidx = (a->buffidx + 1) & a->idxmask;
|
||||
fftw_execute (a->crev);
|
||||
memcpy (a->fftin, &(a->fftin[2 * a->size]), a->size * sizeof(dcomplex));
|
||||
}
|
||||
|
||||
void FIRCORE::setBuffers_fircore (FIRCORE *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
deplan_fircore (a);
|
||||
plan_fircore (a);
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void FIRCORE::setSize_fircore (FIRCORE *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
deplan_fircore (a);
|
||||
plan_fircore (a);
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void FIRCORE::setImpulse_fircore (FIRCORE *a, double* impulse, int update)
|
||||
{
|
||||
memcpy (a->impulse, impulse, a->nc * sizeof (dcomplex));
|
||||
calc_fircore (a, update);
|
||||
}
|
||||
|
||||
void FIRCORE::setNc_fircore (FIRCORE *a, int nc, double* 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 double[a->nc * 2]; // (double *) malloc0 (a->nc * sizeof (complex));
|
||||
a->impulse = new double[a->nc * 2]; // (double *) malloc0 (a->nc * sizeof (complex));
|
||||
memcpy (a->impulse, impulse, a->nc * sizeof (dcomplex));
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void FIRCORE::setMp_fircore (FIRCORE *a, int mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void FIRCORE::setUpdate_fircore (FIRCORE *a)
|
||||
{
|
||||
if (a->masks_ready)
|
||||
{
|
||||
a->update.lock();
|
||||
a->cset = 1 - a->cset;
|
||||
a->update.unlock();
|
||||
a->masks_ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
200
wdsp/firmin.hpp
Normal file
200
wdsp/firmin.hpp
Normal file
@ -0,0 +1,200 @@
|
||||
/* firmin.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 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
|
||||
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Time-Domain FIR *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_firmin_h
|
||||
#define wdsp_firmin_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API FIRMIN
|
||||
{
|
||||
public:
|
||||
int run; // run control
|
||||
int position; // position at which to execute
|
||||
int size; // input/output buffer size, power of two
|
||||
double* in; // input buffer
|
||||
double* out; // output buffer, can be same as input
|
||||
int nc; // number of filter coefficients, power of two
|
||||
double f_low; // low cutoff frequency
|
||||
double f_high; // high cutoff frequency
|
||||
double* ring; // internal complex ring buffer
|
||||
double* h; // complex filter coefficients
|
||||
int rsize; // ring size, number of complex samples, power of two
|
||||
int mask; // mask to update indexes
|
||||
int idx; // ring input/output index
|
||||
double samplerate; // sample rate
|
||||
int wintype; // filter window type
|
||||
double gain; // filter gain
|
||||
|
||||
static FIRMIN* create_firmin (int run, int position, int size, double* in, double* out,
|
||||
int nc, double f_low, double f_high, int samplerate, int wintype, double gain);
|
||||
static void destroy_firmin (FIRMIN *a);
|
||||
static void flush_firmin (FIRMIN *a);
|
||||
static void xfirmin (FIRMIN *a, int pos);
|
||||
static void setBuffers_firmin (FIRMIN *a, double* in, double* out);
|
||||
static void setSamplerate_firmin (FIRMIN *a, int rate);
|
||||
static void setSize_firmin (FIRMIN *a, int size);
|
||||
static void setFreqs_firmin (FIRMIN *a, double f_low, double f_high);
|
||||
|
||||
private:
|
||||
static void calc_firmin (FIRMIN *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Standalone Partitioned Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_firopt_h
|
||||
#define wdsp_firopt_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API FIROPT
|
||||
{
|
||||
int run; // run control
|
||||
int position; // position at which to execute
|
||||
int size; // input/output buffer size, power of two
|
||||
double* in; // input buffer
|
||||
double* out; // output buffer, can be same as input
|
||||
int nc; // number of filter coefficients, power of two, >= size
|
||||
double f_low; // low cutoff frequency
|
||||
double f_high; // high cutoff frequency
|
||||
double samplerate; // sample rate
|
||||
int wintype; // filter window type
|
||||
double gain; // filter gain
|
||||
int nfor; // number of buffers in delay line
|
||||
double* fftin; // fft input buffer
|
||||
double** fmask; // frequency domain masks
|
||||
double** fftout; // fftout delay line
|
||||
double* accum; // frequency domain accumulator
|
||||
int buffidx; // fft out buffer index
|
||||
int idxmask; // mask for index computations
|
||||
double* maskgen; // input for mask generation FFT
|
||||
fftw_plan* pcfor; // array of forward FFT plans
|
||||
fftw_plan crev; // reverse fft plan
|
||||
fftw_plan* maskplan; // plans for frequency domain masks
|
||||
|
||||
static FIROPT* create_firopt (int run, int position, int size, double* in, double* out,
|
||||
int nc, double f_low, double f_high, int samplerate, int wintype, double gain);
|
||||
static void xfiropt (FIROPT *a, int pos);
|
||||
static void destroy_firopt (FIROPT *a);
|
||||
static void flush_firopt (FIROPT *a);
|
||||
static void setBuffers_firopt (FIROPT *a, double* in, double* out);
|
||||
static void setSamplerate_firopt (FIROPT *a, int rate);
|
||||
static void setSize_firopt (FIROPT *a, int size);
|
||||
static void setFreqs_firopt (FIROPT *a, double f_low, double f_high);
|
||||
|
||||
private:
|
||||
static void plan_firopt (FIROPT *a);
|
||||
static void calc_firopt (FIROPT *a);
|
||||
static void deplan_firopt (FIROPT *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Filter Kernel *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_fircore_h
|
||||
#define wdsp_fircore_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API FIRCORE
|
||||
{
|
||||
public:
|
||||
int size; // input/output buffer size, power of two
|
||||
double* in; // input buffer
|
||||
double* out; // output buffer, can be same as input
|
||||
int nc; // number of filter coefficients, power of two, >= size
|
||||
double* impulse; // impulse response of filter
|
||||
double* imp;
|
||||
int nfor; // number of buffers in delay line
|
||||
double* fftin; // fft input buffer
|
||||
double*** fmask; // frequency domain masks
|
||||
double** fftout; // fftout delay line
|
||||
double* accum; // frequency domain accumulator
|
||||
int buffidx; // fft out buffer index
|
||||
int idxmask; // mask for index computations
|
||||
double* maskgen; // input for mask generation FFT
|
||||
fftw_plan* pcfor; // array of forward FFT plans
|
||||
fftw_plan crev; // reverse fft plan
|
||||
fftw_plan** maskplan; // plans for frequency domain masks
|
||||
QRecursiveMutex update;
|
||||
int cset;
|
||||
int mp;
|
||||
int masks_ready;
|
||||
|
||||
static FIRCORE* create_fircore (int size, double* in, double* out,
|
||||
int nc, int mp, double* impulse);
|
||||
static void xfircore (FIRCORE *a);
|
||||
static void destroy_fircore (FIRCORE *a);
|
||||
static void flush_fircore (FIRCORE *a);
|
||||
static void setBuffers_fircore (FIRCORE *a, double* in, double* out);
|
||||
static void setSize_fircore (FIRCORE *a, int size);
|
||||
static void setImpulse_fircore (FIRCORE *a, double* impulse, int update);
|
||||
static void setNc_fircore (FIRCORE *a, int nc, double* impulse);
|
||||
static void setMp_fircore (FIRCORE *a, int mp);
|
||||
static void setUpdate_fircore (FIRCORE *a);
|
||||
|
||||
private:
|
||||
static void plan_fircore (FIRCORE *a);
|
||||
static void calc_fircore (FIRCORE *a, int flip);
|
||||
static void deplan_fircore (FIRCORE *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
406
wdsp/fmd.cpp
Normal file
406
wdsp/fmd.cpp
Normal file
@ -0,0 +1,406 @@
|
||||
/* fmd.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 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 "iir.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "fcurve.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "wcpAGC.hpp"
|
||||
#include "fmd.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void FMD::calc_fmd (FMD *a)
|
||||
{
|
||||
// pll
|
||||
a->omega_min = TWOPI * a->fmin / a->rate;
|
||||
a->omega_max = TWOPI * a->fmax / a->rate;
|
||||
a->g1 = 1.0 - exp(-2.0 * a->omegaN * a->zeta / a->rate);
|
||||
a->g2 = -a->g1 + 2.0 * (1 - exp(-a->omegaN * a->zeta / a->rate) * cos(a->omegaN / a->rate * sqrt(1.0 - a->zeta * a->zeta)));
|
||||
a->phs = 0.0;
|
||||
a->fil_out = 0.0;
|
||||
a->omega = 0.0;
|
||||
a->pllpole = a->omegaN * sqrt(2.0 * a->zeta * a->zeta + 1.0 + sqrt((2.0 * a->zeta * a->zeta + 1.0) * (2.0 * a->zeta * a->zeta + 1.0) + 1)) / TWOPI;
|
||||
// dc removal
|
||||
a->mtau = exp(-1.0 / (a->rate * a->tau));
|
||||
a->onem_mtau = 1.0 - a->mtau;
|
||||
a->fmdc = 0.0;
|
||||
// pll audio gain
|
||||
a->again = a->rate / (a->deviation * TWOPI);
|
||||
// CTCSS Removal
|
||||
a->sntch = SNOTCH::create_snotch(1, a->size, a->out, a->out, (int)a->rate, a->ctcss_freq, 0.0002);
|
||||
// detector limiter
|
||||
a->plim = WCPAGC::create_wcpagc (
|
||||
1, // run - always ON
|
||||
5, // mode
|
||||
1, // 0 for max(I,Q), 1 for envelope
|
||||
a->out, // input buff pointer
|
||||
a->out, // output buff pointer
|
||||
a->size, // io_buffsize
|
||||
(int)a->rate, // sample rate
|
||||
0.001, // tau_attack
|
||||
0.008, // tau_decay
|
||||
4, // n_tau
|
||||
a->lim_gain, // max_gain (sets threshold, initial value)
|
||||
1.0, // var_gain / slope
|
||||
1.0, // fixed_gain
|
||||
1.0, // max_input
|
||||
0.9, // out_targ
|
||||
0.250, // tau_fast_backaverage
|
||||
0.004, // tau_fast_decay
|
||||
4.0, // pop_ratio
|
||||
0, // hang_enable
|
||||
0.500, // tau_hang_backmult
|
||||
0.500, // hangtime
|
||||
2.000, // hang_thresh
|
||||
0.100); // tau_hang_decay
|
||||
}
|
||||
|
||||
void FMD::decalc_fmd (FMD *a)
|
||||
{
|
||||
WCPAGC::destroy_wcpagc(a->plim);
|
||||
SNOTCH::destroy_snotch(a->sntch);
|
||||
}
|
||||
|
||||
FMD* FMD::create_fmd(
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int rate,
|
||||
double deviation,
|
||||
double f_low,
|
||||
double f_high,
|
||||
double fmin,
|
||||
double fmax,
|
||||
double zeta,
|
||||
double omegaN,
|
||||
double tau,
|
||||
double afgain,
|
||||
int sntch_run,
|
||||
double ctcss_freq,
|
||||
int nc_de,
|
||||
int mp_de,
|
||||
int nc_aud,
|
||||
int mp_aud
|
||||
)
|
||||
{
|
||||
FMD *a = new FMD;
|
||||
double* impulse;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = (double)rate;
|
||||
a->deviation = deviation;
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
a->fmin = fmin;
|
||||
a->fmax = fmax;
|
||||
a->zeta = zeta;
|
||||
a->omegaN = omegaN;
|
||||
a->tau = tau;
|
||||
a->afgain = afgain;
|
||||
a->sntch_run = sntch_run;
|
||||
a->ctcss_freq = ctcss_freq;
|
||||
a->nc_de = nc_de;
|
||||
a->mp_de = mp_de;
|
||||
a->nc_aud = nc_aud;
|
||||
a->mp_aud = mp_aud;
|
||||
a->lim_run = 0;
|
||||
a->lim_pre_gain = 0.4;
|
||||
a->lim_gain = 2.5;
|
||||
calc_fmd (a);
|
||||
// de-emphasis filter
|
||||
a->audio = new double[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex));
|
||||
impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
a->pde = FIRCORE::create_fircore (a->size, a->audio, a->out, a->nc_de, a->mp_de, impulse);
|
||||
delete[] (impulse);
|
||||
// audio filter
|
||||
impulse = FIR::fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||
a->paud = FIRCORE::create_fircore (a->size, a->out, a->out, a->nc_aud, a->mp_aud, impulse);
|
||||
delete[] (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void FMD::destroy_fmd (FMD *a)
|
||||
{
|
||||
FIRCORE::destroy_fircore (a->paud);
|
||||
FIRCORE::destroy_fircore (a->pde);
|
||||
delete[] (a->audio);
|
||||
decalc_fmd (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FMD::flush_fmd (FMD *a)
|
||||
{
|
||||
memset (a->audio, 0, a->size * sizeof (dcomplex));
|
||||
FIRCORE::flush_fircore (a->pde);
|
||||
FIRCORE::flush_fircore (a->paud);
|
||||
a->phs = 0.0;
|
||||
a->fil_out = 0.0;
|
||||
a->omega = 0.0;
|
||||
a->fmdc = 0.0;
|
||||
SNOTCH::flush_snotch (a->sntch);
|
||||
WCPAGC::flush_wcpagc (a->plim);
|
||||
}
|
||||
|
||||
void FMD::xfmd (FMD *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double det, del_out;
|
||||
double vco[2], corr[2];
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
// pll
|
||||
vco[0] = cos (a->phs);
|
||||
vco[1] = sin (a->phs);
|
||||
corr[0] = + a->in[2 * i + 0] * vco[0] + a->in[2 * i + 1] * vco[1];
|
||||
corr[1] = - a->in[2 * i + 0] * vco[1] + a->in[2 * i + 1] * vco[0];
|
||||
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;
|
||||
if (a->omega < a->omega_min) a->omega = a->omega_min;
|
||||
if (a->omega > a->omega_max) a->omega = a->omega_max;
|
||||
a->fil_out = a->g1 * det + a->omega;
|
||||
a->phs += del_out;
|
||||
while (a->phs >= TWOPI) a->phs -= TWOPI;
|
||||
while (a->phs < 0.0) a->phs += TWOPI;
|
||||
// dc removal, gain, & demod output
|
||||
a->fmdc = a->mtau * a->fmdc + a->onem_mtau * a->fil_out;
|
||||
a->audio[2 * i + 0] = a->again * (a->fil_out - a->fmdc);
|
||||
a->audio[2 * i + 1] = a->audio[2 * i + 0];
|
||||
}
|
||||
// de-emphasis
|
||||
FIRCORE::xfircore (a->pde);
|
||||
// audio filter
|
||||
FIRCORE::xfircore (a->paud);
|
||||
// CTCSS Removal
|
||||
SNOTCH::xsnotch (a->sntch);
|
||||
if (a->lim_run)
|
||||
{
|
||||
for (i = 0; i < 2 * a->size; i++)
|
||||
a->out[i] *= a->lim_pre_gain;
|
||||
WCPAGC::xwcpagc (a->plim);
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void FMD::setBuffers_fmd (FMD *a, double* in, double* out)
|
||||
{
|
||||
decalc_fmd (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_fmd (a);
|
||||
FIRCORE::setBuffers_fircore (a->pde, a->audio, a->out);
|
||||
FIRCORE::setBuffers_fircore (a->paud, a->out, a->out);
|
||||
WCPAGC::setBuffers_wcpagc (a->plim, a->out, a->out);
|
||||
}
|
||||
|
||||
void FMD::setSamplerate_fmd (FMD *a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
decalc_fmd (a);
|
||||
a->rate = rate;
|
||||
calc_fmd (a);
|
||||
// de-emphasis filter
|
||||
impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
FIRCORE::setImpulse_fircore (a->pde, impulse, 1);
|
||||
delete[] (impulse);
|
||||
// audio filter
|
||||
impulse = FIR::fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->paud, impulse, 1);
|
||||
delete[] (impulse);
|
||||
WCPAGC::setSamplerate_wcpagc (a->plim, (int)a->rate);
|
||||
}
|
||||
|
||||
void FMD::setSize_fmd (FMD *a, int size)
|
||||
{
|
||||
double* impulse;
|
||||
decalc_fmd (a);
|
||||
delete[] (a->audio);
|
||||
a->size = size;
|
||||
calc_fmd (a);
|
||||
a->audio = new double[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex));
|
||||
// de-emphasis filter
|
||||
FIRCORE::destroy_fircore (a->pde);
|
||||
impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
a->pde = FIRCORE::create_fircore (a->size, a->audio, a->out, a->nc_de, a->mp_de, impulse);
|
||||
delete[] (impulse);
|
||||
// audio filter
|
||||
FIRCORE::destroy_fircore (a->paud);
|
||||
impulse = FIR::fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||
a->paud = FIRCORE::create_fircore (a->size, a->out, a->out, a->nc_aud, a->mp_aud, impulse);
|
||||
delete[] (impulse);
|
||||
WCPAGC::setSize_wcpagc (a->plim, a->size);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void FMD::SetFMDeviation (RXA& rxa, double deviation)
|
||||
{
|
||||
FMD *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.fmd.p;
|
||||
a->deviation = deviation;
|
||||
a->again = a->rate / (a->deviation * TWOPI);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetCTCSSFreq (RXA& rxa, double freq)
|
||||
{
|
||||
FMD *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.fmd.p;
|
||||
a->ctcss_freq = freq;
|
||||
SNOTCH::SetSNCTCSSFreq (a->sntch, a->ctcss_freq);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetCTCSSRun (RXA& rxa, int run)
|
||||
{
|
||||
FMD *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.fmd.p;
|
||||
a->sntch_run = run;
|
||||
SNOTCH::SetSNCTCSSRun (a->sntch, a->sntch_run);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetFMNCde (RXA& rxa, int nc)
|
||||
{
|
||||
FMD *a;
|
||||
double* impulse;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.fmd.p;
|
||||
if (a->nc_de != nc)
|
||||
{
|
||||
a->nc_de = nc;
|
||||
impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
FIRCORE::setNc_fircore (a->pde, a->nc_de, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetFMMPde (RXA& rxa, int mp)
|
||||
{
|
||||
FMD *a;
|
||||
a = rxa.fmd.p;
|
||||
if (a->mp_de != mp)
|
||||
{
|
||||
a->mp_de = mp;
|
||||
FIRCORE::setMp_fircore (a->pde, a->mp_de);
|
||||
}
|
||||
}
|
||||
|
||||
void FMD::SetFMNCaud (RXA& rxa, int nc)
|
||||
{
|
||||
FMD *a;
|
||||
double* impulse;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.fmd.p;
|
||||
if (a->nc_aud != nc)
|
||||
{
|
||||
a->nc_aud = nc;
|
||||
impulse = FIR::fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||
FIRCORE::setNc_fircore (a->paud, a->nc_aud, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetFMMPaud (RXA& rxa, int mp)
|
||||
{
|
||||
FMD *a;
|
||||
a = rxa.fmd.p;
|
||||
if (a->mp_aud != mp)
|
||||
{
|
||||
a->mp_aud = mp;
|
||||
FIRCORE::setMp_fircore (a->paud, a->mp_aud);
|
||||
}
|
||||
}
|
||||
|
||||
void FMD::SetFMLimRun (RXA& rxa, int run)
|
||||
{
|
||||
FMD *a;
|
||||
a = rxa.fmd.p;
|
||||
rxa.csDSP.lock();
|
||||
if (a->lim_run != run)
|
||||
{
|
||||
a->lim_run = run;
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetFMLimGain (RXA& rxa, double gaindB)
|
||||
{
|
||||
double gain = pow(10.0, gaindB / 20.0);
|
||||
FMD *a = rxa.fmd.p;
|
||||
rxa.csDSP.lock();
|
||||
if (a->lim_gain != gain)
|
||||
{
|
||||
decalc_fmd(a);
|
||||
a->lim_gain = gain;
|
||||
calc_fmd(a);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMD::SetFMAFFilter(RXA& rxa, double low, double high)
|
||||
{
|
||||
FMD *a = rxa.fmd.p;
|
||||
double* impulse;
|
||||
rxa.csDSP.lock();
|
||||
if (a->f_low != low || a->f_high != high)
|
||||
{
|
||||
a->f_low = low;
|
||||
a->f_high = high;
|
||||
// de-emphasis filter
|
||||
impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
FIRCORE::setImpulse_fircore (a->pde, impulse, 1);
|
||||
delete[] (impulse);
|
||||
// audio filter
|
||||
impulse = FIR::fir_bandpass (a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->paud, impulse, 1);
|
||||
delete[] (impulse);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
137
wdsp/fmd.hpp
Normal file
137
wdsp/fmd.hpp
Normal file
@ -0,0 +1,137 @@
|
||||
/* fmd.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_fmd_h
|
||||
#define wdsp_fmd_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class SNOTCH;
|
||||
class WCPAGC;
|
||||
class RXA;
|
||||
|
||||
class WDSP_API FMD
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double f_low; // audio low cutoff
|
||||
double f_high; // audio high cutoff
|
||||
// pll
|
||||
double fmin; // pll - minimum carrier freq to lock
|
||||
double fmax; // pll - maximum carrier freq to lock
|
||||
double omega_min; // pll - minimum lock check parameter
|
||||
double omega_max; // pll - maximum lock check parameter
|
||||
double zeta; // pll - damping factor; as coded, must be <=1.0
|
||||
double omegaN; // pll - natural frequency
|
||||
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 pllpole; // pll - pole frequency
|
||||
// for dc removal
|
||||
double tau;
|
||||
double mtau;
|
||||
double onem_mtau;
|
||||
double fmdc;
|
||||
// pll audio gain
|
||||
double deviation;
|
||||
double again;
|
||||
// for de-emphasis filter
|
||||
double* audio;
|
||||
FIRCORE *pde;
|
||||
int nc_de;
|
||||
int mp_de;
|
||||
// for audio filter
|
||||
FIRCORE *paud;
|
||||
int nc_aud;
|
||||
int mp_aud;
|
||||
double afgain;
|
||||
// CTCSS removal
|
||||
SNOTCH *sntch;
|
||||
int sntch_run;
|
||||
double ctcss_freq;
|
||||
// detector limiter
|
||||
WCPAGC *plim;
|
||||
int lim_run;
|
||||
double lim_gain;
|
||||
double lim_pre_gain;
|
||||
|
||||
static FMD* create_fmd (
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int rate,
|
||||
double deviation,
|
||||
double f_low,
|
||||
double f_high,
|
||||
double fmin,
|
||||
double fmax,
|
||||
double zeta,
|
||||
double omegaN,
|
||||
double tau,
|
||||
double afgain,
|
||||
int sntch_run,
|
||||
double ctcss_freq,
|
||||
int nc_de,
|
||||
int mp_de,
|
||||
int nc_aud,
|
||||
int mp_aud
|
||||
);
|
||||
static void destroy_fmd (FMD *a);
|
||||
static void flush_fmd (FMD *a);
|
||||
static void xfmd (FMD *a);
|
||||
static void setBuffers_fmd (FMD *a, double* in, double* out);
|
||||
static void setSamplerate_fmd (FMD *a, int rate);
|
||||
static void setSize_fmd (FMD *a, int size);
|
||||
// RXA Properties
|
||||
static void SetFMDeviation (RXA& rxa, double deviation);
|
||||
static void SetCTCSSFreq (RXA& rxa, double freq);
|
||||
static void SetCTCSSRun (RXA& rxa, int run);
|
||||
static void SetFMNCde (RXA& rxa, int nc);
|
||||
static void SetFMMPde (RXA& rxa, int mp);
|
||||
static void SetFMNCaud (RXA& rxa, int nc);
|
||||
static void SetFMMPaud (RXA& rxa, int mp);
|
||||
static void SetFMLimRun (RXA& rxa, int run);
|
||||
static void SetFMLimGain (RXA& rxa, double gaindB);
|
||||
static void SetFMAFFilter(RXA& rxa, double low, double high);
|
||||
|
||||
private:
|
||||
static void calc_fmd (FMD *a);
|
||||
static void decalc_fmd (FMD *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
249
wdsp/fmmod.cpp
Normal file
249
wdsp/fmmod.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
/* fmmod.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 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 "firmin.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "fmmod.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void FMMOD::calc_fmmod (FMMOD *a)
|
||||
{
|
||||
// ctcss gen
|
||||
a->tscale = 1.0 / (1.0 + a->ctcss_level);
|
||||
a->tphase = 0.0;
|
||||
a->tdelta = TWOPI * a->ctcss_freq / a->samplerate;
|
||||
// mod
|
||||
a->sphase = 0.0;
|
||||
a->sdelta = TWOPI * a->deviation / a->samplerate;
|
||||
// bandpass
|
||||
a->bp_fc = a->deviation + a->f_high;
|
||||
}
|
||||
|
||||
FMMOD* FMMOD::create_fmmod (
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int rate,
|
||||
double dev,
|
||||
double f_low,
|
||||
double f_high,
|
||||
int ctcss_run,
|
||||
double ctcss_level,
|
||||
double ctcss_freq,
|
||||
int bp_run,
|
||||
int nc,
|
||||
int mp
|
||||
)
|
||||
{
|
||||
FMMOD *a = new FMMOD;
|
||||
double* impulse;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->samplerate = (double)rate;
|
||||
a->deviation = dev;
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
a->ctcss_run = ctcss_run;
|
||||
a->ctcss_level = ctcss_level;
|
||||
a->ctcss_freq = ctcss_freq;
|
||||
a->bp_run = bp_run;
|
||||
a->nc = nc;
|
||||
a->mp = mp;
|
||||
calc_fmmod (a);
|
||||
impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
a->p = FIRCORE::create_fircore (a->size, a->out, a->out, a->nc, a->mp, impulse);
|
||||
delete[] (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void FMMOD::destroy_fmmod (FMMOD *a)
|
||||
{
|
||||
FIRCORE::destroy_fircore (a->p);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FMMOD::flush_fmmod (FMMOD *a)
|
||||
{
|
||||
a->tphase = 0.0;
|
||||
a->sphase = 0.0;
|
||||
}
|
||||
|
||||
void FMMOD::xfmmod (FMMOD *a)
|
||||
{
|
||||
int i;
|
||||
double dp, magdp, peak;
|
||||
if (a->run)
|
||||
{
|
||||
peak = 0.0;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
if (a->ctcss_run)
|
||||
{
|
||||
a->tphase += a->tdelta;
|
||||
if (a->tphase >= TWOPI) a->tphase -= TWOPI;
|
||||
a->out[2 * i + 0] = a->tscale * (a->in[2 * i + 0] + a->ctcss_level * cos (a->tphase));
|
||||
}
|
||||
dp = a->out[2 * i + 0] * a->sdelta;
|
||||
a->sphase += dp;
|
||||
if (a->sphase >= TWOPI) a->sphase -= TWOPI;
|
||||
if (a->sphase < 0.0 ) a->sphase += TWOPI;
|
||||
a->out[2 * i + 0] = 0.7071 * cos (a->sphase);
|
||||
a->out[2 * i + 1] = 0.7071 * sin (a->sphase);
|
||||
if ((magdp = dp) < 0.0) magdp = - magdp;
|
||||
if (magdp > peak) peak = magdp;
|
||||
}
|
||||
//print_deviation ("peakdev.txt", peak, a->samplerate);
|
||||
if (a->bp_run)
|
||||
FIRCORE::xfircore (a->p);
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void FMMOD::setBuffers_fmmod (FMMOD *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_fmmod (a);
|
||||
FIRCORE::setBuffers_fircore (a->p, a->out, a->out);
|
||||
}
|
||||
|
||||
void FMMOD::setSamplerate_fmmod (FMMOD *a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
a->samplerate = rate;
|
||||
calc_fmmod (a);
|
||||
impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
|
||||
delete[] (impulse);
|
||||
}
|
||||
|
||||
void FMMOD::setSize_fmmod (FMMOD *a, int size)
|
||||
{
|
||||
double* impulse;
|
||||
a->size = size;
|
||||
calc_fmmod (a);
|
||||
FIRCORE::setSize_fircore (a->p, a->size);
|
||||
impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
|
||||
delete[] (impulse);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void FMMOD::SetFMDeviation (TXA& txa, double deviation)
|
||||
{
|
||||
FMMOD *a = txa.fmmod.p;
|
||||
double bp_fc = a->f_high + deviation;
|
||||
double* impulse = FIR::fir_bandpass (a->nc, -bp_fc, +bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 0);
|
||||
delete[] (impulse);
|
||||
txa.csDSP.lock();
|
||||
a->deviation = deviation;
|
||||
// mod
|
||||
a->sphase = 0.0;
|
||||
a->sdelta = TWOPI * a->deviation / a->samplerate;
|
||||
// bandpass
|
||||
a->bp_fc = bp_fc;
|
||||
FIRCORE::setUpdate_fircore (a->p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMMOD::SetCTCSSFreq (TXA& txa, double freq)
|
||||
{
|
||||
FMMOD *a;
|
||||
txa.csDSP.lock();
|
||||
a = txa.fmmod.p;
|
||||
a->ctcss_freq = freq;
|
||||
a->tphase = 0.0;
|
||||
a->tdelta = TWOPI * a->ctcss_freq / a->samplerate;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMMOD::SetCTCSSRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.fmmod.p->ctcss_run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMMOD::SetFMNC (TXA& txa, int nc)
|
||||
{
|
||||
FMMOD *a;
|
||||
double* impulse;
|
||||
txa.csDSP.lock();
|
||||
a = txa.fmmod.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = FIR::fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMMOD::SetFMMP (TXA& txa, int mp)
|
||||
{
|
||||
FMMOD *a;
|
||||
a = txa.fmmod.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
FIRCORE::setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
void FMMOD::SetFMAFFreqs (TXA& txa, double low, double high)
|
||||
{
|
||||
FMMOD *a;
|
||||
double* impulse;
|
||||
txa.csDSP.lock();
|
||||
a = txa.fmmod.p;
|
||||
if (a->f_low != low || a->f_high != high)
|
||||
{
|
||||
a->f_low = low;
|
||||
a->f_high = high;
|
||||
a->bp_fc = a->deviation + a->f_high;
|
||||
impulse = FIR::fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
FIRCORE::setImpulse_fircore (a->p, impulse, 1);
|
||||
delete[] (impulse);
|
||||
}
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
102
wdsp/fmmod.hpp
Normal file
102
wdsp/fmmod.hpp
Normal file
@ -0,0 +1,102 @@
|
||||
/* fmmod.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_fmmod_h
|
||||
#define wdsp_fmmod_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API FMMOD
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double samplerate;
|
||||
double deviation;
|
||||
double f_low;
|
||||
double f_high;
|
||||
int ctcss_run;
|
||||
double ctcss_level;
|
||||
double ctcss_freq;
|
||||
// for ctcss gen
|
||||
double tscale;
|
||||
double tphase;
|
||||
double tdelta;
|
||||
// mod
|
||||
double sphase;
|
||||
double sdelta;
|
||||
// bandpass
|
||||
int bp_run;
|
||||
double bp_fc;
|
||||
int nc;
|
||||
int mp;
|
||||
FIRCORE *p;
|
||||
|
||||
static FMMOD* create_fmmod (
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int rate,
|
||||
double dev,
|
||||
double f_low,
|
||||
double f_high,
|
||||
int ctcss_run,
|
||||
double ctcss_level,
|
||||
double ctcss_freq,
|
||||
int bp_run,
|
||||
int nc,
|
||||
int mp
|
||||
);
|
||||
static void destroy_fmmod (FMMOD *a);
|
||||
static void flush_fmmod (FMMOD *a);
|
||||
static void xfmmod (FMMOD *a);
|
||||
static void setBuffers_fmmod (FMMOD *a, double* in, double* out);
|
||||
static void setSamplerate_fmmod (FMMOD *a, int rate);
|
||||
static void setSize_fmmod (FMMOD *a, int size);
|
||||
// TXA Properties
|
||||
static void SetFMDeviation (TXA& txa, double deviation);
|
||||
static void SetCTCSSFreq (TXA& txa, double freq);
|
||||
static void SetCTCSSRun (TXA& txa, int run);
|
||||
static void SetFMMP (TXA& txa, int mp);
|
||||
static void SetFMNC (TXA& txa, int nc);
|
||||
static void SetFMAFFreqs (TXA& txa, double low, double high);
|
||||
|
||||
private:
|
||||
static void calc_fmmod (FMMOD *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
302
wdsp/fmsq.cpp
Normal file
302
wdsp/fmsq.cpp
Normal file
@ -0,0 +1,302 @@
|
||||
/* fmsq.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "eq.hpp"
|
||||
#include "fmsq.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void FMSQ::calc_fmsq (FMSQ *a)
|
||||
{
|
||||
double delta, theta;
|
||||
double* impulse;
|
||||
int i;
|
||||
// noise filter
|
||||
a->noise = new double[2 * a->size * 2]; // (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->F[0] = 0.0;
|
||||
a->F[1] = a->fc;
|
||||
a->F[2] = *a->pllpole;
|
||||
a->F[3] = 20000.0;
|
||||
a->G[0] = 0.0;
|
||||
a->G[1] = 0.0;
|
||||
a->G[2] = 3.0;
|
||||
a->G[3] = +20.0 * log10(20000.0 / *a->pllpole);
|
||||
impulse = EQP::eq_impulse (a->nc, 3, a->F, a->G, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
a->p = FIRCORE::create_fircore (a->size, a->trigger, a->noise, a->nc, a->mp, impulse);
|
||||
delete[] (impulse);
|
||||
// noise averaging
|
||||
a->avm = exp(-1.0 / (a->rate * a->avtau));
|
||||
a->onem_avm = 1.0 - a->avm;
|
||||
a->avnoise = 100.0;
|
||||
a->longavm = exp(-1.0 / (a->rate * a->longtau));
|
||||
a->onem_longavm = 1.0 - a->longavm;
|
||||
a->longnoise = 1.0;
|
||||
// level change
|
||||
a->ntup = (int)(a->tup * a->rate);
|
||||
a->ntdown = (int)(a->tdown * a->rate);
|
||||
a->cup = new double[a->ntup + 1]; // (double *)malloc0 ((a->ntup + 1) * sizeof(double));
|
||||
a->cdown = new double[a->ntdown + 1]; //(double *)malloc0 ((a->ntdown + 1) * sizeof(double));
|
||||
delta = PI / (double)a->ntup;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntup; i++)
|
||||
{
|
||||
a->cup[i] = 0.5 * (1.0 - cos(theta));
|
||||
theta += delta;
|
||||
}
|
||||
delta = PI / (double)a->ntdown;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntdown; i++)
|
||||
{
|
||||
a->cdown[i] = 0.5 * (1 + cos(theta));
|
||||
theta += delta;
|
||||
}
|
||||
// control
|
||||
a->state = 0;
|
||||
a->ready = 0;
|
||||
a->ramp = 0.0;
|
||||
a->rstep = 1.0 / a->rate;
|
||||
}
|
||||
|
||||
void FMSQ::decalc_fmsq (FMSQ *a)
|
||||
{
|
||||
delete[] (a->cdown);
|
||||
delete[] (a->cup);
|
||||
FIRCORE::destroy_fircore (a->p);
|
||||
delete[] (a->noise);
|
||||
}
|
||||
|
||||
FMSQ* FMSQ::create_fmsq (
|
||||
int run,
|
||||
int size,
|
||||
double* insig,
|
||||
double* outsig,
|
||||
double* trigger,
|
||||
int rate,
|
||||
double fc,
|
||||
double* pllpole,
|
||||
double tdelay,
|
||||
double avtau,
|
||||
double longtau,
|
||||
double tup,
|
||||
double tdown,
|
||||
double tail_thresh,
|
||||
double unmute_thresh,
|
||||
double min_tail,
|
||||
double max_tail,
|
||||
int nc,
|
||||
int mp
|
||||
)
|
||||
{
|
||||
FMSQ *a = new FMSQ;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->insig = insig;
|
||||
a->outsig = outsig;
|
||||
a->trigger = trigger;
|
||||
a->rate = (double)rate;
|
||||
a->fc = fc;
|
||||
a->pllpole = pllpole;
|
||||
a->tdelay = tdelay;
|
||||
a->avtau = avtau;
|
||||
a->longtau = longtau;
|
||||
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;
|
||||
a->nc = nc;
|
||||
a->mp = mp;
|
||||
calc_fmsq (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void FMSQ::destroy_fmsq (FMSQ *a)
|
||||
{
|
||||
decalc_fmsq (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FMSQ::flush_fmsq (FMSQ *a)
|
||||
{
|
||||
FIRCORE::flush_fircore (a->p);
|
||||
a->avnoise = 100.0;
|
||||
a->longnoise = 1.0;
|
||||
a->state = 0;
|
||||
a->ready = 0;
|
||||
a->ramp = 0.0;
|
||||
}
|
||||
|
||||
enum _fmsqstate
|
||||
{
|
||||
MUTED,
|
||||
INCREASE,
|
||||
UNMUTED,
|
||||
TAIL,
|
||||
DECREASE
|
||||
};
|
||||
|
||||
void FMSQ::xfmsq (FMSQ *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double noise, lnlimit;
|
||||
FIRCORE::xfircore (a->p);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
noise = sqrt(a->noise[2 * i + 0] * a->noise[2 * i + 0] + a->noise[2 * i + 1] * a->noise[2 * i + 1]);
|
||||
a->avnoise = a->avm * a->avnoise + a->onem_avm * noise;
|
||||
a->longnoise = a->longavm * a->longnoise + a->onem_longavm * noise;
|
||||
if (!a->ready) a->ramp += a->rstep;
|
||||
if (a->ramp >= a->tdelay) a->ready = 1;
|
||||
|
||||
switch (a->state)
|
||||
{
|
||||
case MUTED:
|
||||
if (a->avnoise < a->unmute_thresh && a->ready)
|
||||
{
|
||||
a->state = INCREASE;
|
||||
a->count = a->ntup;
|
||||
}
|
||||
a->outsig[2 * i + 0] = 0.0;
|
||||
a->outsig[2 * i + 1] = 0.0;
|
||||
break;
|
||||
case INCREASE:
|
||||
a->outsig[2 * i + 0] = a->insig[2 * i + 0] * a->cup[a->ntup - a->count];
|
||||
a->outsig[2 * i + 1] = a->insig[2 * i + 1] * a->cup[a->ntup - a->count];
|
||||
if (a->count-- == 0)
|
||||
a->state = UNMUTED;
|
||||
break;
|
||||
case UNMUTED:
|
||||
if (a->avnoise > a->tail_thresh)
|
||||
{
|
||||
a->state = TAIL;
|
||||
if ((lnlimit = a->longnoise) > 1.0) lnlimit = 1.0;
|
||||
a->count = (int)((a->min_tail + (a->max_tail - a->min_tail) * lnlimit) * a->rate);
|
||||
}
|
||||
a->outsig[2 * i + 0] = a->insig[2 * i + 0];
|
||||
a->outsig[2 * i + 1] = a->insig[2 * i + 1];
|
||||
break;
|
||||
case TAIL:
|
||||
a->outsig[2 * i + 0] = a->insig[2 * i + 0];
|
||||
a->outsig[2 * i + 1] = a->insig[2 * i + 1];
|
||||
if (a->avnoise < a->unmute_thresh)
|
||||
a->state = UNMUTED;
|
||||
else if (a->count-- == 0)
|
||||
{
|
||||
a->state = DECREASE;
|
||||
a->count = a->ntdown;
|
||||
}
|
||||
break;
|
||||
case DECREASE:
|
||||
a->outsig[2 * i + 0] = a->insig[2 * i + 0] * a->cdown[a->ntdown - a->count];
|
||||
a->outsig[2 * i + 1] = a->insig[2 * i + 1] * a->cdown[a->ntdown - a->count];
|
||||
if (a->count-- == 0)
|
||||
a->state = MUTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->insig != a->outsig)
|
||||
memcpy (a->outsig, a->insig, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void FMSQ::setBuffers_fmsq (FMSQ *a, double* in, double* out, double* trig)
|
||||
{
|
||||
a->insig = in;
|
||||
a->outsig = out;
|
||||
a->trigger = trig;
|
||||
FIRCORE::setBuffers_fircore (a->p, a->trigger, a->noise);
|
||||
}
|
||||
|
||||
void FMSQ::setSamplerate_fmsq (FMSQ *a, int rate)
|
||||
{
|
||||
decalc_fmsq (a);
|
||||
a->rate = rate;
|
||||
calc_fmsq (a);
|
||||
}
|
||||
|
||||
void FMSQ::setSize_fmsq (FMSQ *a, int size)
|
||||
{
|
||||
decalc_fmsq (a);
|
||||
a->size = size;
|
||||
calc_fmsq (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void FMSQ::SetFMSQRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.fmsq.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMSQ::SetFMSQThreshold (RXA& rxa, double threshold)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.fmsq.p->tail_thresh = threshold;
|
||||
rxa.fmsq.p->unmute_thresh = 0.9 * threshold;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMSQ::SetFMSQNC (RXA& rxa, int nc)
|
||||
{
|
||||
FMSQ *a;
|
||||
double* impulse;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.fmsq.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = EQP::eq_impulse (a->nc, 3, a->F, a->G, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
FIRCORE::setNc_fircore (a->p, a->nc, impulse);
|
||||
delete[] (impulse);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void FMSQ::SetFMSQMP (RXA& rxa, int mp)
|
||||
{
|
||||
FMSQ *a;
|
||||
a = rxa.fmsq.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
FIRCORE::setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
120
wdsp/fmsq.hpp
Normal file
120
wdsp/fmsq.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
/* fmsq.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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_fmsq_h
|
||||
#define wdsp_fmsq_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class RXA;
|
||||
|
||||
class WDSP_API FMSQ
|
||||
{
|
||||
public:
|
||||
int run; // 0 if squelch system is OFF; 1 if it's ON
|
||||
int size; // size of input/output buffers
|
||||
double* insig; // squelch input signal buffer
|
||||
double* outsig; // squelch output signal buffer
|
||||
double* trigger; // buffer used to trigger mute/unmute (may be same as input; matches timing of input buffer)
|
||||
double rate; // sample rate
|
||||
double* noise;
|
||||
double fc; // corner frequency for sig / noise detection
|
||||
double* pllpole; // pointer to pole frequency of the fm demodulator pll
|
||||
double F[4];
|
||||
double G[4];
|
||||
double avtau; // time constant for averaging noise
|
||||
double avm;
|
||||
double onem_avm;
|
||||
double avnoise;
|
||||
double longtau; // time constant for long averaging
|
||||
double longavm;
|
||||
double onem_longavm;
|
||||
double longnoise;
|
||||
int state; // state machine control
|
||||
int count;
|
||||
double tup;
|
||||
double tdown;
|
||||
int ntup;
|
||||
int ntdown;
|
||||
double* cup;
|
||||
double* cdown;
|
||||
double tail_thresh;
|
||||
double unmute_thresh;
|
||||
double min_tail;
|
||||
double max_tail;
|
||||
int ready;
|
||||
double ramp;
|
||||
double rstep;
|
||||
double tdelay;
|
||||
int nc;
|
||||
int mp;
|
||||
FIRCORE *p;
|
||||
|
||||
static FMSQ* create_fmsq (
|
||||
int run,
|
||||
int size,
|
||||
double* insig,
|
||||
double* outsig,
|
||||
double* trigger,
|
||||
int rate,
|
||||
double fc,
|
||||
double* pllpole,
|
||||
double tdelay,
|
||||
double avtau,
|
||||
double longtau,
|
||||
double tup,
|
||||
double tdown,
|
||||
double tail_thresh,
|
||||
double unmute_thresh,
|
||||
double min_tail,
|
||||
double max_tail,
|
||||
int nc,
|
||||
int mp
|
||||
);
|
||||
static void destroy_fmsq (FMSQ *a);
|
||||
static void flush_fmsq (FMSQ *a);
|
||||
static void xfmsq (FMSQ *a);
|
||||
static void setBuffers_fmsq (FMSQ *a, double* in, double* out, double* trig);
|
||||
static void setSamplerate_fmsq (FMSQ *a, int rate);
|
||||
static void setSize_fmsq (FMSQ *a, int size);
|
||||
// RXA Properties
|
||||
static void SetFMSQRun (RXA& rxa, int run);
|
||||
static void SetFMSQThreshold (RXA& rxa, double threshold);
|
||||
static void SetFMSQNC (RXA& rxa, int nc);
|
||||
static void SetFMSQMP (RXA& rxa, int mp);
|
||||
|
||||
private:
|
||||
static void calc_fmsq (FMSQ *a);
|
||||
static void decalc_fmsq (FMSQ *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
122
wdsp/gain.cpp
Normal file
122
wdsp/gain.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/* gain.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "gain.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
GAIN* GAIN::create_gain (int run, int* prun, int size, double* in, double* out, double Igain, double Qgain)
|
||||
{
|
||||
GAIN *a = new GAIN;
|
||||
a->run = run;
|
||||
a->prun = prun;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->Igain = Igain;
|
||||
a->Qgain = Qgain;
|
||||
return a;
|
||||
}
|
||||
|
||||
void GAIN::destroy_gain (GAIN *a)
|
||||
{
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void GAIN::flush_gain (GAIN *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GAIN::xgain (GAIN *a)
|
||||
{
|
||||
int srun;
|
||||
a->cs_update.lock();
|
||||
if (a->prun != 0)
|
||||
srun = *(a->prun);
|
||||
else
|
||||
srun = 1;
|
||||
if (a->run && srun)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->out[2 * i + 0] = a->Igain * a->in[2 * i + 0];
|
||||
a->out[2 * i + 1] = a->Qgain * a->in[2 * i + 1];
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
void GAIN::setBuffers_gain (GAIN *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void GAIN::setSamplerate_gain (GAIN *, int)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GAIN::setSize_gain (GAIN *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* POINTER-BASED PROPERTIES *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void GAIN::pSetTXOutputLevel (GAIN *a, double level)
|
||||
{
|
||||
a->cs_update.lock();
|
||||
a->Igain = level;
|
||||
a->Qgain = level;
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
void GAIN::pSetTXOutputLevelRun (GAIN *a, int run)
|
||||
{
|
||||
a->cs_update.lock();
|
||||
a->run = run;
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
void GAIN::pSetTXOutputLevelSize (GAIN *a, int size)
|
||||
{
|
||||
a->cs_update.lock();
|
||||
a->size = size;
|
||||
a->cs_update.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
65
wdsp/gain.hpp
Normal file
65
wdsp/gain.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* gain.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdso_gain_h
|
||||
#define wdsp_gain_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API GAIN
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int* prun;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double Igain;
|
||||
double Qgain;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static GAIN* create_gain (int run, int* prun, int size, double* in, double* out, double Igain, double Qgain);
|
||||
static void destroy_gain (GAIN *a);
|
||||
static void flush_gain (GAIN *a);
|
||||
static void xgain (GAIN *a);
|
||||
static void setBuffers_gain (GAIN *a, double* in, double* out);
|
||||
static void setSamplerate_gain (GAIN *a, int rate);
|
||||
static void setSize_gain (GAIN *a, int size);
|
||||
// TXA Properties
|
||||
// POINTER-BASED Properties
|
||||
static void pSetTXOutputLevel (GAIN *a, double level);
|
||||
static void pSetTXOutputLevelRun (GAIN *a, int run);
|
||||
static void pSetTXOutputLevelSize (GAIN *a, int size);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
660
wdsp/gen.cpp
Normal file
660
wdsp/gen.cpp
Normal file
@ -0,0 +1,660 @@
|
||||
/* gen.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "gen.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void GEN::calc_tone (GEN *a)
|
||||
{
|
||||
a->tone.phs = 0.0;
|
||||
a->tone.delta = TWOPI * a->tone.freq / a->rate;
|
||||
a->tone.cosdelta = cos (a->tone.delta);
|
||||
a->tone.sindelta = sin (a->tone.delta);
|
||||
}
|
||||
|
||||
void GEN::calc_tt (GEN *a)
|
||||
{
|
||||
a->tt.phs1 = 0.0;
|
||||
a->tt.phs2 = 0.0;
|
||||
a->tt.delta1 = TWOPI * a->tt.f1 / a->rate;
|
||||
a->tt.delta2 = TWOPI * a->tt.f2 / a->rate;
|
||||
a->tt.cosdelta1 = cos (a->tt.delta1);
|
||||
a->tt.cosdelta2 = cos (a->tt.delta2);
|
||||
a->tt.sindelta1 = sin (a->tt.delta1);
|
||||
a->tt.sindelta2 = sin (a->tt.delta2);
|
||||
}
|
||||
|
||||
void GEN::calc_sweep (GEN *a)
|
||||
{
|
||||
a->sweep.phs = 0.0;
|
||||
a->sweep.dphs = TWOPI * a->sweep.f1 / a->rate;
|
||||
a->sweep.d2phs = TWOPI * a->sweep.sweeprate / (a->rate * a->rate);
|
||||
a->sweep.dphsmax = TWOPI * a->sweep.f2 / a->rate;
|
||||
}
|
||||
|
||||
void GEN::calc_sawtooth (GEN *a)
|
||||
{
|
||||
a->saw.period = 1.0 / a->saw.f;
|
||||
a->saw.delta = 1.0 / a->rate;
|
||||
a->saw.t = 0.0;
|
||||
}
|
||||
|
||||
void GEN::calc_triangle (GEN *a)
|
||||
{
|
||||
a->tri.period = 1.0 / a->tri.f;
|
||||
a->tri.half = 0.5 * a->tri.period;
|
||||
a->tri.delta = 1.0 / a->rate;
|
||||
a->tri.t = 0.0;
|
||||
a->tri.t1 = 0.0;
|
||||
}
|
||||
|
||||
void GEN::calc_pulse (GEN *a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
a->pulse.pperiod = 1.0 / a->pulse.pf;
|
||||
a->pulse.tphs = 0.0;
|
||||
a->pulse.tdelta = TWOPI * a->pulse.tf / a->rate;
|
||||
a->pulse.tcosdelta = cos (a->pulse.tdelta);
|
||||
a->pulse.tsindelta = sin (a->pulse.tdelta);
|
||||
a->pulse.pntrans = (int)(a->pulse.ptranstime * a->rate);
|
||||
a->pulse.pnon = (int)(a->pulse.pdutycycle * a->pulse.pperiod * a->rate);
|
||||
a->pulse.pnoff = (int)(a->pulse.pperiod * a->rate) - a->pulse.pnon - 2 * a->pulse.pntrans;
|
||||
if (a->pulse.pnoff < 0) a->pulse.pnoff = 0;
|
||||
a->pulse.pcount = a->pulse.pnoff;
|
||||
a->pulse.state = 0;
|
||||
a->pulse.ctrans = new double[a->pulse.pntrans + 1]; // (double *) malloc0 ((a->pulse.pntrans + 1) * sizeof (double));
|
||||
delta = PI / (double)a->pulse.pntrans;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->pulse.pntrans; i++)
|
||||
{
|
||||
a->pulse.ctrans[i] = 0.5 * (1.0 - cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void GEN::calc_gen (GEN *a)
|
||||
{
|
||||
calc_tone (a);
|
||||
calc_tt (a);
|
||||
calc_sweep (a);
|
||||
calc_sawtooth (a);
|
||||
calc_triangle (a);
|
||||
calc_pulse (a);
|
||||
}
|
||||
|
||||
void GEN::decalc_gen (GEN *a)
|
||||
{
|
||||
delete[] (a->pulse.ctrans);
|
||||
}
|
||||
|
||||
GEN* GEN::create_gen (int run, int size, double* in, double* out, int rate, int mode)
|
||||
{
|
||||
GEN *a = new GEN;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = (double)rate;
|
||||
a->mode = mode;
|
||||
// tone
|
||||
a->tone.mag = 1.0;
|
||||
a->tone.freq = 1000.0;
|
||||
// two-tone
|
||||
a->tt.mag1 = 0.5;
|
||||
a->tt.mag2 = 0.5;
|
||||
a->tt.f1 = + 900.0;
|
||||
a->tt.f2 = + 1700.0;
|
||||
// noise
|
||||
srand ((unsigned int)time (0));
|
||||
a->noise.mag = 1.0;
|
||||
// sweep
|
||||
a->sweep.mag = 1.0;
|
||||
a->sweep.f1 = -20000.0;
|
||||
a->sweep.f2 = +20000.0;
|
||||
a->sweep.sweeprate = +4000.0;
|
||||
// sawtooth
|
||||
a->saw.mag = 1.0;
|
||||
a->saw.f = 500.0;
|
||||
// triangle
|
||||
a->tri.mag = 1.0;
|
||||
a->tri.f = 500.0;
|
||||
// pulse
|
||||
a->pulse.mag = 1.0;
|
||||
a->pulse.pf = 0.25;
|
||||
a->pulse.pdutycycle = 0.25;
|
||||
a->pulse.ptranstime = 0.002;
|
||||
a->pulse.tf = 1000.0;
|
||||
calc_gen (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void GEN::destroy_gen (GEN *a)
|
||||
{
|
||||
decalc_gen (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void GEN::flush_gen (GEN *a)
|
||||
{
|
||||
a->pulse.state = 0;
|
||||
}
|
||||
|
||||
enum pstate
|
||||
{
|
||||
OFF,
|
||||
UP,
|
||||
ON,
|
||||
DOWN
|
||||
};
|
||||
|
||||
void GEN::xgen (GEN *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
switch (a->mode)
|
||||
{
|
||||
case 0: // tone
|
||||
{
|
||||
int i;
|
||||
double t1, t2;
|
||||
double cosphase = cos (a->tone.phs);
|
||||
double sinphase = sin (a->tone.phs);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->out[2 * i + 0] = + a->tone.mag * cosphase;
|
||||
a->out[2 * i + 1] = - a->tone.mag * sinphase;
|
||||
t1 = cosphase;
|
||||
t2 = sinphase;
|
||||
cosphase = t1 * a->tone.cosdelta - t2 * a->tone.sindelta;
|
||||
sinphase = t1 * a->tone.sindelta + t2 * a->tone.cosdelta;
|
||||
a->tone.phs += a->tone.delta;
|
||||
if (a->tone.phs >= TWOPI) a->tone.phs -= TWOPI;
|
||||
if (a->tone.phs < 0.0 ) a->tone.phs += TWOPI;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: // two-tone
|
||||
{
|
||||
int i;
|
||||
double tcos, tsin;
|
||||
double cosphs1 = cos (a->tt.phs1);
|
||||
double sinphs1 = sin (a->tt.phs1);
|
||||
double cosphs2 = cos (a->tt.phs2);
|
||||
double sinphs2 = sin (a->tt.phs2);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->out[2 * i + 0] = + a->tt.mag1 * cosphs1 + a->tt.mag2 * cosphs2;
|
||||
a->out[2 * i + 1] = - a->tt.mag1 * sinphs1 - a->tt.mag2 * sinphs2;
|
||||
tcos = cosphs1;
|
||||
tsin = sinphs1;
|
||||
cosphs1 = tcos * a->tt.cosdelta1 - tsin * a->tt.sindelta1;
|
||||
sinphs1 = tcos * a->tt.sindelta1 + tsin * a->tt.cosdelta1;
|
||||
a->tt.phs1 += a->tt.delta1;
|
||||
if (a->tt.phs1 >= TWOPI) a->tt.phs1 -= TWOPI;
|
||||
if (a->tt.phs1 < 0.0 ) a->tt.phs1 += TWOPI;
|
||||
tcos = cosphs2;
|
||||
tsin = sinphs2;
|
||||
cosphs2 = tcos * a->tt.cosdelta2 - tsin * a->tt.sindelta2;
|
||||
sinphs2 = tcos * a->tt.sindelta2 + tsin * a->tt.cosdelta2;
|
||||
a->tt.phs2 += a->tt.delta2;
|
||||
if (a->tt.phs2 >= TWOPI) a->tt.phs2 -= TWOPI;
|
||||
if (a->tt.phs2 < 0.0 ) a->tt.phs2 += TWOPI;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: // noise
|
||||
{
|
||||
int i;
|
||||
double r1, r2, c, rad;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
do
|
||||
{
|
||||
r1 = 2.0 * (double)rand() / (double)RAND_MAX - 1.0;
|
||||
r2 = 2.0 * (double)rand() / (double)RAND_MAX - 1.0;
|
||||
c = r1 * r1 + r2 * r2;
|
||||
} while (c >= 1.0);
|
||||
rad = sqrt (-2.0 * log (c) / c);
|
||||
a->out[2 * i + 0] = a->noise.mag * rad * r1;
|
||||
a->out[2 * i + 1] = a->noise.mag * rad * r2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: // sweep
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->out[2 * i + 0] = + a->sweep.mag * cos(a->sweep.phs);
|
||||
a->out[2 * i + 1] = - a->sweep.mag * sin(a->sweep.phs);
|
||||
a->sweep.phs += a->sweep.dphs;
|
||||
a->sweep.dphs += a->sweep.d2phs;
|
||||
if (a->sweep.phs >= TWOPI) a->sweep.phs -= TWOPI;
|
||||
if (a->sweep.phs < 0.0 ) a->sweep.phs += TWOPI;
|
||||
if (a->sweep.dphs > a->sweep.dphsmax)
|
||||
a->sweep.dphs = TWOPI * a->sweep.f1 / a->rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: // sawtooth (audio only)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
if (a->saw.t > a->saw.period) a->saw.t -= a->saw.period;
|
||||
a->out[2 * i + 0] = a->saw.mag * (a->saw.t * a->saw.f - 1.0);
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
a->saw.t += a->saw.delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 5: // triangle (audio only)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
if (a->tri.t > a->tri.period) a->tri.t1 = a->tri.t -= a->tri.period;
|
||||
if (a->tri.t > a->tri.half) a->tri.t1 -= a->tri.delta;
|
||||
else a->tri.t1 += a->tri.delta;
|
||||
a->out[2 * i + 0] = a->tri.mag * (4.0 * a->tri.t1 * a->tri.f - 1.0);
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
a->tri.t += a->tri.delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6: // pulse (audio only)
|
||||
{
|
||||
int i;
|
||||
double t1, t2;
|
||||
double cosphase = cos (a->pulse.tphs);
|
||||
double sinphase = sin (a->pulse.tphs);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
if (a->pulse.pnoff != 0)
|
||||
switch (a->pulse.state)
|
||||
{
|
||||
case OFF:
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = UP;
|
||||
a->pulse.pcount = a->pulse.pntrans;
|
||||
}
|
||||
break;
|
||||
case UP:
|
||||
a->out[2 * i + 0] = a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pntrans - a->pulse.pcount];
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = ON;
|
||||
a->pulse.pcount = a->pulse.pnon;
|
||||
}
|
||||
break;
|
||||
case ON:
|
||||
a->out[2 * i + 0] = a->pulse.mag * cosphase;
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = DOWN;
|
||||
a->pulse.pcount = a->pulse.pntrans;
|
||||
}
|
||||
break;
|
||||
case DOWN:
|
||||
a->out[2 * i + 0] = a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pcount];
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = OFF;
|
||||
a->pulse.pcount = a->pulse.pnoff;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
t1 = cosphase;
|
||||
t2 = sinphase;
|
||||
cosphase = t1 * a->pulse.tcosdelta - t2 * a->pulse.tsindelta;
|
||||
sinphase = t1 * a->pulse.tsindelta + t2 * a->pulse.tcosdelta;
|
||||
a->pulse.tphs += a->pulse.tdelta;
|
||||
if (a->pulse.tphs >= TWOPI) a->pulse.tphs -= TWOPI;
|
||||
if (a->pulse.tphs < 0.0 ) a->pulse.tphs += TWOPI;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // silence
|
||||
{
|
||||
memset (a->out, 0, a->size * sizeof (dcomplex));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void GEN::setBuffers_gen (GEN *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void GEN::setSamplerate_gen (GEN *a, int rate)
|
||||
{
|
||||
decalc_gen (a);
|
||||
a->rate = rate;
|
||||
calc_gen (a);
|
||||
}
|
||||
|
||||
void GEN::setSize_gen (GEN *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_gen (a);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// 'PreGen', gen0
|
||||
|
||||
void GEN::SetPreGenRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenMode (RXA& rxa, int mode)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->mode = mode;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenToneMag (RXA& rxa, double mag)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->tone.mag = mag;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenToneFreq (RXA& rxa, double freq)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->tone.freq = freq;
|
||||
calc_tone (rxa.gen0.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenNoiseMag (RXA& rxa, double mag)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->noise.mag = mag;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSweepMag (RXA& rxa, double mag)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->sweep.mag = mag;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSweepFreq (RXA& rxa, double freq1, double freq2)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->sweep.f1 = freq1;
|
||||
rxa.gen0.p->sweep.f2 = freq2;
|
||||
calc_sweep (rxa.gen0.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSweepRate (RXA& rxa, double rate)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.gen0.p->sweep.sweeprate = rate;
|
||||
calc_sweep (rxa.gen0.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// 'PreGen', gen0
|
||||
|
||||
void GEN::SetPreGenRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenMode (TXA& txa, int mode)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->mode = mode;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenToneMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->tone.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenToneFreq (TXA& txa, double freq)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->tone.freq = freq;
|
||||
calc_tone (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenNoiseMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->noise.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSweepMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->sweep.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSweepFreq (TXA& txa, double freq1, double freq2)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->sweep.f1 = freq1;
|
||||
txa.gen0.p->sweep.f2 = freq2;
|
||||
calc_sweep (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSweepRate (TXA& txa, double rate)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->sweep.sweeprate = rate;
|
||||
calc_sweep (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSawtoothMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->saw.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenSawtoothFreq (TXA& txa, double freq)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->saw.f = freq;
|
||||
calc_sawtooth (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenTriangleMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->tri.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenTriangleFreq (TXA& txa, double freq)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->tri.f = freq;
|
||||
calc_triangle (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenPulseMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->pulse.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenPulseFreq (TXA& txa, double freq)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->pulse.pf = freq;
|
||||
calc_pulse (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenPulseDutyCycle (TXA& txa, double dc)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->pulse.pdutycycle = dc;
|
||||
calc_pulse (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenPulseToneFreq (TXA& txa, double freq)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->pulse.tf = freq;
|
||||
calc_pulse (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPreGenPulseTransition (TXA& txa, double transtime)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen0.p->pulse.ptranstime = transtime;
|
||||
calc_pulse (txa.gen0.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
// 'PostGen', gen1
|
||||
|
||||
void GEN::SetPostGenRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenMode (TXA& txa, int mode)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->mode = mode;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenToneMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->tone.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenToneFreq (TXA& txa, double freq)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->tone.freq = freq;
|
||||
calc_tone (txa.gen1.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenTTMag (TXA& txa, double mag1, double mag2)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->tt.mag1 = mag1;
|
||||
txa.gen1.p->tt.mag2 = mag2;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenTTFreq (TXA& txa, double freq1, double freq2)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->tt.f1 = freq1;
|
||||
txa.gen1.p->tt.f2 = freq2;
|
||||
calc_tt (txa.gen1.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenSweepMag (TXA& txa, double mag)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->sweep.mag = mag;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenSweepFreq (TXA& txa, double freq1, double freq2)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->sweep.f1 = freq1;
|
||||
txa.gen1.p->sweep.f2 = freq2;
|
||||
calc_sweep (txa.gen1.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void GEN::SetPostGenSweepRate (TXA& txa, double rate)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.gen1.p->sweep.sweeprate = rate;
|
||||
calc_sweep (txa.gen1.p);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
183
wdsp/gen.hpp
Normal file
183
wdsp/gen.hpp
Normal file
@ -0,0 +1,183 @@
|
||||
/* gen.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_gen_h
|
||||
#define wdsp_gen_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API GEN
|
||||
{
|
||||
public:
|
||||
int run; // run
|
||||
int size; // number of samples per buffer
|
||||
double* in; // input buffer (retained in case I want to mix in a generated signal)
|
||||
double* out; // output buffer
|
||||
double rate; // sample rate
|
||||
int mode;
|
||||
struct _tone
|
||||
{
|
||||
double mag;
|
||||
double freq;
|
||||
double phs;
|
||||
double delta;
|
||||
double cosdelta;
|
||||
double sindelta;
|
||||
} tone;
|
||||
struct _tt
|
||||
{
|
||||
double mag1;
|
||||
double mag2;
|
||||
double f1;
|
||||
double f2;
|
||||
double phs1;
|
||||
double phs2;
|
||||
double delta1;
|
||||
double delta2;
|
||||
double cosdelta1;
|
||||
double cosdelta2;
|
||||
double sindelta1;
|
||||
double sindelta2;
|
||||
} tt;
|
||||
struct _noise
|
||||
{
|
||||
double mag;
|
||||
} noise;
|
||||
struct _sweep
|
||||
{
|
||||
double mag;
|
||||
double f1;
|
||||
double f2;
|
||||
double sweeprate;
|
||||
double phs;
|
||||
double dphs;
|
||||
double d2phs;
|
||||
double dphsmax;
|
||||
} sweep;
|
||||
struct _saw
|
||||
{
|
||||
double mag;
|
||||
double f;
|
||||
double period;
|
||||
double delta;
|
||||
double t;
|
||||
} saw;
|
||||
struct _tri
|
||||
{
|
||||
double mag;
|
||||
double f;
|
||||
double period;
|
||||
double half;
|
||||
double delta;
|
||||
double t;
|
||||
double t1;
|
||||
} tri;
|
||||
struct _pulse
|
||||
{
|
||||
double mag;
|
||||
double pf;
|
||||
double pdutycycle;
|
||||
double ptranstime;
|
||||
double* ctrans;
|
||||
int pcount;
|
||||
int pnon;
|
||||
int pntrans;
|
||||
int pnoff;
|
||||
double pperiod;
|
||||
double tf;
|
||||
double tphs;
|
||||
double tdelta;
|
||||
double tcosdelta;
|
||||
double tsindelta;
|
||||
int state;
|
||||
} pulse;
|
||||
|
||||
static GEN* create_gen (int run, int size, double* in, double* out, int rate, int mode);
|
||||
static void destroy_gen (GEN *a);
|
||||
static void flush_gen (GEN *a);
|
||||
static void xgen (GEN *a);
|
||||
static void setBuffers_gen (GEN *a, double* in, double* out);
|
||||
static void setSamplerate_gen (GEN *a, int rate);
|
||||
static void setSize_gen (GEN *a, int size);
|
||||
// RXA Properties
|
||||
static void SetPreGenRun (RXA& rxa, int run);
|
||||
static void SetPreGenMode (RXA& rxa, int mode);
|
||||
static void SetPreGenToneMag (RXA& rxa, double mag);
|
||||
static void SetPreGenToneFreq (RXA& rxa, double freq);
|
||||
static void SetPreGenNoiseMag (RXA& rxa, double mag);
|
||||
static void SetPreGenSweepMag (RXA& rxa, double mag);
|
||||
static void SetPreGenSweepFreq (RXA& rxa, double freq1, double freq2);
|
||||
static void SetPreGenSweepRate (RXA& rxa, double rate);
|
||||
// TXA Properties
|
||||
static void SetPreGenRun (TXA& txa, int run);
|
||||
static void SetPreGenMode (TXA& txa, int mode);
|
||||
static void SetPreGenToneMag (TXA& txa, double mag);
|
||||
static void SetPreGenToneFreq (TXA& txa, double freq);
|
||||
static void SetPreGenNoiseMag (TXA& txa, double mag);
|
||||
static void SetPreGenSweepMag (TXA& txa, double mag);
|
||||
static void SetPreGenSweepFreq (TXA& txal, double freq1, double freq2);
|
||||
static void SetPreGenSweepRate (TXA& txa, double rate);
|
||||
static void SetPreGenSawtoothMag (TXA& txa, double mag);
|
||||
static void SetPreGenSawtoothFreq (TXA& txa, double freq);
|
||||
static void SetPreGenTriangleMag (TXA& txa, double mag);
|
||||
static void SetPreGenTriangleFreq (TXA& txa, double freq);
|
||||
static void SetPreGenPulseMag (TXA& txa, double mag);
|
||||
static void SetPreGenPulseFreq (TXA& txa, double freq);
|
||||
static void SetPreGenPulseDutyCycle (TXA& txa, double dc);
|
||||
static void SetPreGenPulseToneFreq (TXA& txa, double freq);
|
||||
static void SetPreGenPulseTransition (TXA& txa, double transtime);
|
||||
// 'PostGen', gen1
|
||||
static void SetPostGenRun (TXA& txa, int run);
|
||||
static void SetPostGenMode (TXA& txa, int mode);
|
||||
static void SetPostGenToneMag (TXA& txa, double mag);
|
||||
static void SetPostGenToneFreq (TXA& txa, double freq);
|
||||
static void SetPostGenTTMag (TXA& txa, double mag1, double mag2);
|
||||
static void SetgenRun (TXA& txa, int run);
|
||||
static void SetPostGenTTFreq (TXA& txa, double freq1, double freq2);
|
||||
static void SetPostGenSweepMag (TXA& txa, double mag);
|
||||
static void SetPostGenSweepFreq (TXA& txa, double freq1, double freq2);
|
||||
static void SetPostGenSweepRate (TXA& txa, double rate);
|
||||
|
||||
private:
|
||||
static void calc_tone (GEN *a);
|
||||
static void calc_tt (GEN *a);
|
||||
static void calc_sweep (GEN *a);
|
||||
static void calc_sawtooth (GEN *a);
|
||||
static void calc_triangle (GEN *a);
|
||||
static void calc_pulse (GEN *a);
|
||||
static void calc_gen (GEN *a);
|
||||
static void decalc_gen (GEN *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
258
wdsp/icfir.cpp
Normal file
258
wdsp/icfir.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/* icfir.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2018 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@pratt.one
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "icfir.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void ICFIR::calc_icfir (ICFIR *a)
|
||||
{
|
||||
double* impulse;
|
||||
a->scale = 1.0 / (double)(2 * a->size);
|
||||
impulse = icfir_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);
|
||||
}
|
||||
|
||||
void ICFIR::decalc_icfir (ICFIR *a)
|
||||
{
|
||||
FIRCORE::destroy_fircore (a->p);
|
||||
}
|
||||
|
||||
ICFIR* ICFIR::create_icfir (
|
||||
int run,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* 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
|
||||
// nc: number of filter coefficients
|
||||
// mp: minimum phase flag
|
||||
// in: pointer to the input buffer
|
||||
// out: pointer to the output buffer
|
||||
// rate: samplerate
|
||||
// DD: differential delay of the CIC to be compensated (usually 1 or 2)
|
||||
// R: interpolation factor of CIC
|
||||
// Pairs: number of comb-integrator pairs in the CIC
|
||||
// cutoff: cutoff frequency
|
||||
// xtype: 0 - fourth power transition; 1 - raised cosine transition
|
||||
// xbw: width of raised cosine transition
|
||||
{
|
||||
ICFIR *a = new ICFIR;
|
||||
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_icfir (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void ICFIR::destroy_icfir (ICFIR *a)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
delete[] (a);
|
||||
}
|
||||
|
||||
void ICFIR::flush_icfir (ICFIR *a)
|
||||
{
|
||||
FIRCORE::flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void ICFIR::xicfir (ICFIR *a)
|
||||
{
|
||||
if (a->run)
|
||||
FIRCORE::xfircore (a->p);
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void ICFIR::setBuffers_icfir (ICFIR *a, double* in, double* out)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
void ICFIR::setSamplerate_icfir (ICFIR *a, int rate)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->runrate = rate;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
void ICFIR::setSize_icfir (ICFIR *a, int size)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->size = size;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
void ICFIR::setOutRate_icfir (ICFIR *a, int rate)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->cicrate = rate;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
double* ICFIR::icfir_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
|
||||
// DD: differential delay used in the CIC filter
|
||||
// R: interpolation / decimation factor of the CIC
|
||||
// Pairs: number of comb-integrator pairs in the CIC
|
||||
// runrate: sample rate at which this filter is to run (assumes there may be flat interp. between this filter and the CIC)
|
||||
// cicrate: sample rate at interface to CIC
|
||||
// cutoff: cutoff frequency
|
||||
// xtype: transition type, 0 for 4th-power rolloff, 1 for raised cosine
|
||||
// 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;
|
||||
double* impulse;
|
||||
double* A = new double[N]; // (double *) malloc0 (N * sizeof (double));
|
||||
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 * (double)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N
|
||||
double* xistion = new double[x_samps + 1]; // (double *) malloc0 ((x_samps + 1) * sizeof (double));
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||
{
|
||||
fn = ri / (L * (double)N);
|
||||
if (fn <= ft)
|
||||
{
|
||||
if (fn == 0.0) tmp = 1.0;
|
||||
else if ((tmp = sin (PI * DD * fn) / (DD * R * sin (PI * fn / R))) < 0.0)
|
||||
tmp = -tmp;
|
||||
mag = pow (tmp, Pairs) * local_scale;
|
||||
}
|
||||
else
|
||||
mag *= (ft * ft * ft * ft) / (fn * fn * fn * fn);
|
||||
A[i] = mag;
|
||||
}
|
||||
}
|
||||
else if (xtype == 1)
|
||||
{
|
||||
for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0)
|
||||
{
|
||||
fn = ri / (L *(double)N);
|
||||
if (i < c_samps)
|
||||
{
|
||||
if (fn == 0.0) tmp = 1.0;
|
||||
else if ((tmp = sin (PI * DD * fn) / (DD * R * sin (PI * fn / R))) < 0.0)
|
||||
tmp = -tmp;
|
||||
mag = pow (tmp, Pairs) * local_scale;
|
||||
A[i] = mag;
|
||||
}
|
||||
else if ( i >= c_samps && i <= c_samps + x_samps)
|
||||
A[i] = mag * xistion[i - c_samps];
|
||||
else
|
||||
A[i] = 0.0;
|
||||
}
|
||||
}
|
||||
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++)
|
||||
A[i] = A[u_samps - j];
|
||||
impulse = FIR::fir_fsamp (N, A, rtype, 1.0, wintype);
|
||||
// print_impulse ("icfirImpulse.txt", N, impulse, 1, 0);
|
||||
delete[] (A);
|
||||
delete[] xistion;
|
||||
return impulse;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
//PORT void
|
||||
//SetTXAICFIRRun (int channel, int run)
|
||||
//{
|
||||
// EnterCriticalSection(&ch[channel].csDSP);
|
||||
// txa[channel].icfir.p->run = run;
|
||||
// LeaveCriticalSection(&ch[channel].csDSP);
|
||||
//}
|
||||
|
||||
} // namespace WDSP
|
||||
104
wdsp/icfir.hpp
Normal file
104
wdsp/icfir.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
/* icfir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2018 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@pratt.one
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_icfir_h
|
||||
#define wdsp_icfir_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
|
||||
class WDSP_API ICFIR
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
int nc;
|
||||
int mp;
|
||||
double* in;
|
||||
double* out;
|
||||
int runrate;
|
||||
int cicrate;
|
||||
int DD;
|
||||
int R;
|
||||
int Pairs;
|
||||
double cutoff;
|
||||
double scale;
|
||||
int xtype;
|
||||
double xbw;
|
||||
int wintype;
|
||||
FIRCORE *p;
|
||||
|
||||
static ICFIR* create_icfir (
|
||||
int run,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* out,
|
||||
int runrate,
|
||||
int cicrate,
|
||||
int DD,
|
||||
int R,
|
||||
int Pairs,
|
||||
double cutoff,
|
||||
int xtype,
|
||||
double xbw,
|
||||
int wintype
|
||||
);
|
||||
static void destroy_icfir (ICFIR *a);
|
||||
static void flush_icfir (ICFIR *a);
|
||||
static void xicfir (ICFIR *a);
|
||||
static void setBuffers_icfir (ICFIR *a, double* in, double* out);
|
||||
static void setSamplerate_icfir (ICFIR *a, int rate);
|
||||
static void setSize_icfir (ICFIR *a, int size);
|
||||
static void setOutRate_icfir (ICFIR *a, int rate);
|
||||
static double* icfir_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
|
||||
);
|
||||
|
||||
private:
|
||||
static void calc_icfir (ICFIR *a);
|
||||
static void decalc_icfir (ICFIR *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
1356
wdsp/iir.cpp
Normal file
1356
wdsp/iir.cpp
Normal file
File diff suppressed because it is too large
Load Diff
426
wdsp/iir.hpp
Normal file
426
wdsp/iir.hpp
Normal file
@ -0,0 +1,426 @@
|
||||
/* iir.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
|
||||
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Bi-Quad Notch *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_snotch_h
|
||||
#define wdsp_snotch_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API SNOTCH
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double f;
|
||||
double bw;
|
||||
double a0, a1, a2, b1, b2;
|
||||
double x0, x1, x2, y1, y2;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static SNOTCH* create_snotch (int run, int size, double* in, double* out, int rate, double f, double bw);
|
||||
static void destroy_snotch (SNOTCH *a);
|
||||
static void flush_snotch (SNOTCH *a);
|
||||
static void xsnotch (SNOTCH *a);
|
||||
static void setBuffers_snotch (SNOTCH *a, double* in, double* out);
|
||||
static void setSamplerate_snotch (SNOTCH *a, int rate);
|
||||
static void setSize_snotch (SNOTCH *a, int size);
|
||||
static void SetSNCTCSSFreq (SNOTCH *a, double freq);
|
||||
static void SetSNCTCSSRun (SNOTCH *a, int run);
|
||||
|
||||
private:
|
||||
static void calc_snotch (SNOTCH *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Bi-Quad Peaking *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_speak_h
|
||||
#define wdsp_speak_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API SPEAK
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double f;
|
||||
double bw;
|
||||
double cbw;
|
||||
double gain;
|
||||
double fgain;
|
||||
int nstages;
|
||||
int design;
|
||||
double a0, a1, a2, b1, b2;
|
||||
double *x0, *x1, *x2, *y0, *y1, *y2;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static SPEAK* create_speak (int run, int size, double* in, double* out, int rate, double f, double bw, double gain, int nstages, int design);
|
||||
static void destroy_speak (SPEAK *a);
|
||||
static void flush_speak (SPEAK *a);
|
||||
static void xspeak (SPEAK *a);
|
||||
static void setBuffers_speak (SPEAK *a, double* in, double* out);
|
||||
static void setSamplerate_speak (SPEAK *a, int rate);
|
||||
static void setSize_speak (SPEAK *a, int size);
|
||||
// RXA
|
||||
static void SetSPCWRun (RXA& rxa, int run);
|
||||
static void SetSPCWFreq (RXA& rxa, double freq);
|
||||
static void SetSPCWBandwidth (RXA& rxa, double bw);
|
||||
static void SetSPCWGain (RXA& rxa, double gain);
|
||||
static void calc_speak (SPEAK *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Multiple Peaking *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _mpeak_h
|
||||
#define _mpeak_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API MPEAK
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
int rate;
|
||||
int npeaks;
|
||||
int* enable;
|
||||
double* f;
|
||||
double* bw;
|
||||
double* gain;
|
||||
int nstages;
|
||||
SPEAK** pfil;
|
||||
double* tmp;
|
||||
double* mix;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static MPEAK* create_mpeak (int run, int size, double* in, double* out, int rate, int npeaks, int* enable, double* f, double* bw, double* gain, int nstages);
|
||||
static void destroy_mpeak (MPEAK *a);
|
||||
static void flush_mpeak (MPEAK *a);
|
||||
static void xmpeak (MPEAK *a);
|
||||
static void setBuffers_mpeak (MPEAK *a, double* in, double* out);
|
||||
static void setSamplerate_mpeak (MPEAK *a, int rate);
|
||||
static void setSize_mpeak (MPEAK *a, int size);
|
||||
// RXA
|
||||
static void SetmpeakRun (RXA& rxa, int run);
|
||||
static void SetmpeakNpeaks (RXA& rxa, int npeaks);
|
||||
static void SetmpeakFilEnable (RXA& rxa, int fil, int enable);
|
||||
static void SetmpeakFilFreq (RXA& rxa, int fil, double freq);
|
||||
static void SetmpeakFilBw (RXA& rxa, int fil, double bw);
|
||||
static void SetmpeakFilGain (RXA& rxa, int fil, double gain);
|
||||
|
||||
private:
|
||||
static void calc_mpeak (MPEAK *a);
|
||||
static void decalc_mpeak (MPEAK *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Phase Rotator *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_phrot_h
|
||||
#define wdsp_phrot_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API PHROT
|
||||
{
|
||||
public:
|
||||
int reverse;
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
int rate;
|
||||
double fc;
|
||||
int nstages;
|
||||
// normalized such that a0 = 1
|
||||
double a1, b0, b1;
|
||||
double *x0, *x1, *y0, *y1;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static PHROT* create_phrot (int run, int size, double* in, double* out, int rate, double fc, int nstages);
|
||||
static void destroy_phrot (PHROT *a);
|
||||
static void flush_phrot (PHROT *a);
|
||||
static void xphrot (PHROT *a);
|
||||
static void setBuffers_phrot (PHROT *a, double* in, double* out);
|
||||
static void setSamplerate_phrot (PHROT *a, int rate);
|
||||
static void setSize_phrot (PHROT *a, int size);
|
||||
// TXA Properties
|
||||
static void SetPHROTRun (TXA& txa, int run);
|
||||
static void SetPHROTCorner (TXA& txa, double corner);
|
||||
static void SetPHROTNstages (TXA& txa, int nstages);
|
||||
static void SetPHROTReverse (TXA& txa, int reverse);
|
||||
|
||||
private:
|
||||
static void calc_phrot (PHROT *a);
|
||||
static void decalc_phrot (PHROT *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Bi-Quad Low-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_bqlp_h
|
||||
#define wdsp_bqlp_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API BQLP
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double fc;
|
||||
double Q;
|
||||
double gain;
|
||||
int nstages;
|
||||
double a0, a1, a2, b1, b2;
|
||||
double* x0, * x1, * x2, * y0, * y1, * y2;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static BQLP* create_bqlp(int run, int size, double* in, double* out, double rate, double fc, double Q, double gain, int nstages);
|
||||
static void destroy_bqlp(BQLP *a);
|
||||
static void flush_bqlp(BQLP *a);
|
||||
static void xbqlp(BQLP *a);
|
||||
static void setBuffers_bqlp(BQLP *a, double* in, double* out);
|
||||
static void setSamplerate_bqlp(BQLP *a, int rate);
|
||||
static void setSize_bqlp(BQLP *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_bqlp(BQLP *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Double Bi-Quad Low-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_dbqlp_h
|
||||
#define wdsp_dbqlp_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API DBQLP
|
||||
{
|
||||
public:
|
||||
static BQLP* create_dbqlp(int run, int size, double* in, double* out, double rate, double fc, double Q, double gain, int nstages);
|
||||
static void destroy_dbqlp(BQLP *a);
|
||||
static void flush_dbqlp(BQLP *a);
|
||||
static void xdbqlp(BQLP *a);
|
||||
static void setBuffers_dbqlp(BQLP *a, double* in, double* out);
|
||||
static void setSamplerate_dbqlp(BQLP *a, int rate);
|
||||
static void setSize_dbqlp(BQLP *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_dbqlp(BQLP *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Bi-Quad Band-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_bqbp_h
|
||||
#define wdsp_bqbp_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API BQBP
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double gain;
|
||||
int nstages;
|
||||
double a0, a1, a2, b1, b2;
|
||||
double* x0, * x1, * x2, * y0, * y1, * y2;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static BQBP* create_bqbp(int run, int size, double* in, double* out, double rate, double f_low, double f_high, double gain, int nstages);
|
||||
static void destroy_bqbp(BQBP *a);
|
||||
static void flush_bqbp(BQBP *a);
|
||||
static void xbqbp(BQBP *a);
|
||||
static void setBuffers_bqbp(BQBP *a, double* in, double* out);
|
||||
static void setSamplerate_bqbp(BQBP *a, int rate);
|
||||
static void setSize_bqbp(BQBP *a, int size);
|
||||
|
||||
// Double Bi-Quad Band-Pass
|
||||
static BQBP* create_dbqbp(int run, int size, double* in, double* out, double rate, double f_low, double f_high, double gain, int nstages);
|
||||
static void destroy_dbqbp(BQBP *a);
|
||||
static void flush_dbqbp(BQBP *a);
|
||||
static void xdbqbp(BQBP *a);
|
||||
static void setBuffers_dbqbp(BQBP *a, double* in, double* out);
|
||||
static void setSamplerate_dbqbp(BQBP *a, int rate);
|
||||
static void setSize_dbqbp(BQBP *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_bqbp(BQBP *a);
|
||||
static void calc_dbqbp(BQBP *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Double Single-Pole High-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_dsphp_h
|
||||
#define wdsp_dsphp_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API SPHP
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double fc;
|
||||
int nstages;
|
||||
double a1, b0, b1;
|
||||
double* x0, * x1, * y0, * y1;
|
||||
QRecursiveMutex cs_update;
|
||||
|
||||
static SPHP* create_dsphp(int run, int size, double* in, double* out, double rate, double fc, int nstages);
|
||||
static void destroy_dsphp(SPHP *a);
|
||||
static void flush_dsphp(SPHP *a);
|
||||
static void xdsphp(SPHP *a);
|
||||
static void setBuffers_dsphp(SPHP *a, double* in, double* out);
|
||||
static void setSamplerate_dsphp(SPHP *a, int rate);
|
||||
static void setSize_dsphp(SPHP *a, int size);
|
||||
|
||||
// Complex Single-Pole High-Pass
|
||||
static SPHP* create_sphp(int run, int size, double* in, double* out, double rate, double fc, int nstages);
|
||||
static void destroy_sphp(SPHP *a);
|
||||
static void flush_sphp(SPHP *a);
|
||||
static void xsphp(SPHP *a);
|
||||
static void setBuffers_sphp(SPHP *a, double* in, double* out);
|
||||
static void setSamplerate_sphp(SPHP *a, int rate);
|
||||
static void setSize_sphp(SPHP *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_sphp(SPHP *a);
|
||||
static void decalc_sphp(SPHP *a);
|
||||
static void calc_dsphp(SPHP *a);
|
||||
static void decalc_dsphp(SPHP *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
327
wdsp/iqc.cpp
Normal file
327
wdsp/iqc.cpp
Normal file
@ -0,0 +1,327 @@
|
||||
/* iqc.c
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "iqc.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void IQC::size_iqc (IQC *a)
|
||||
{
|
||||
int i;
|
||||
a->t = new double[a->ints + 1]; // (double *) malloc0 ((a->ints + 1) * sizeof(double));
|
||||
for (i = 0; i <= a->ints; i++)
|
||||
a->t[i] = (double)i / (double)a->ints;
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
a->cm[i] = new double[a->ints * 4]; // (double *) malloc0 (a->ints * 4 * sizeof(double));
|
||||
a->cc[i] = new double[a->ints * 4]; // (double *) malloc0 (a->ints * 4 * sizeof(double));
|
||||
a->cs[i] = new double[a->ints * 4]; // (double *) malloc0 (a->ints * 4 * sizeof(double));
|
||||
}
|
||||
a->dog.cpi = new int[a->ints]; // (int *) malloc0 (a->ints * sizeof (int));
|
||||
a->dog.count = 0;
|
||||
a->dog.full_ints = 0;
|
||||
}
|
||||
|
||||
void IQC::desize_iqc (IQC *a)
|
||||
{
|
||||
int i;
|
||||
delete[] (a->dog.cpi);
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
delete[] (a->cm[i]);
|
||||
delete[] (a->cc[i]);
|
||||
delete[] (a->cs[i]);
|
||||
}
|
||||
delete[] (a->t);
|
||||
}
|
||||
|
||||
void IQC::calc_iqc (IQC *a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
a->cset = 0;
|
||||
a->count = 0;
|
||||
a->state = 0;
|
||||
a->busy = 0;
|
||||
a->ntup = (int)(a->tup * a->rate);
|
||||
a->cup = new double[a->ntup + 1]; // (double *) malloc0 ((a->ntup + 1) * sizeof (double));
|
||||
delta = PI / (double)a->ntup;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntup; i++)
|
||||
{
|
||||
a->cup[i] = 0.5 * (1.0 - cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
size_iqc (a);
|
||||
}
|
||||
|
||||
void IQC::decalc_iqc (IQC *a)
|
||||
{
|
||||
desize_iqc (a);
|
||||
delete[] (a->cup);
|
||||
}
|
||||
|
||||
IQC* IQC::create_iqc (int run, int size, double* in, double* out, double rate, int ints, double tup, int spi)
|
||||
{
|
||||
IQC *a = new IQC;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = rate;
|
||||
a->ints = ints;
|
||||
a->tup = tup;
|
||||
a->dog.spi = spi;
|
||||
calc_iqc (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void IQC::destroy_iqc (IQC *a)
|
||||
{
|
||||
decalc_iqc (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void IQC::flush_iqc (IQC *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
enum _iqcstate
|
||||
{
|
||||
RUN = 0,
|
||||
BEGIN,
|
||||
SWAP,
|
||||
END,
|
||||
DONE
|
||||
};
|
||||
|
||||
void IQC::xiqc (IQC *a)
|
||||
{
|
||||
if (a->run == 1)
|
||||
{
|
||||
int i, k, cset, mset;
|
||||
double I, Q, env, dx, ym, yc, ys, PRE0, PRE1;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
I = a->in[2 * i + 0];
|
||||
Q = a->in[2 * i + 1];
|
||||
env = sqrt (I * I + Q * Q);
|
||||
if ((k = (int)(env * a->ints)) > a->ints - 1) k = a->ints - 1;
|
||||
dx = env - a->t[k];
|
||||
cset = a->cset;
|
||||
ym = a->cm[cset][4 * k + 0] + dx * (a->cm[cset][4 * k + 1] + dx * (a->cm[cset][4 * k + 2] + dx * a->cm[cset][4 * k + 3]));
|
||||
yc = a->cc[cset][4 * k + 0] + dx * (a->cc[cset][4 * k + 1] + dx * (a->cc[cset][4 * k + 2] + dx * a->cc[cset][4 * k + 3]));
|
||||
ys = a->cs[cset][4 * k + 0] + dx * (a->cs[cset][4 * k + 1] + dx * (a->cs[cset][4 * k + 2] + dx * a->cs[cset][4 * k + 3]));
|
||||
PRE0 = ym * (I * yc - Q * ys);
|
||||
PRE1 = ym * (I * ys + Q * yc);
|
||||
|
||||
switch (a->state)
|
||||
{
|
||||
case RUN:
|
||||
if (a->dog.cpi[k] != a->dog.spi)
|
||||
if (++a->dog.cpi[k] == a->dog.spi)
|
||||
a->dog.full_ints++;
|
||||
if (a->dog.full_ints == a->ints)
|
||||
{
|
||||
a->dog.cs.lock();
|
||||
++a->dog.count;
|
||||
a->dog.cs.unlock();
|
||||
a->dog.full_ints = 0;
|
||||
memset (a->dog.cpi, 0, a->ints * sizeof (int));
|
||||
}
|
||||
break;
|
||||
case BEGIN:
|
||||
PRE0 = (1.0 - a->cup[a->count]) * I + a->cup[a->count] * PRE0;
|
||||
PRE1 = (1.0 - a->cup[a->count]) * Q + a->cup[a->count] * PRE1;
|
||||
if (a->count++ == a->ntup)
|
||||
{
|
||||
a->state = RUN;
|
||||
a->count = 0;
|
||||
a->busy = 0; // InterlockedBitTestAndReset (&a->busy, 0);
|
||||
}
|
||||
break;
|
||||
case SWAP:
|
||||
mset = 1 - cset;
|
||||
ym = a->cm[mset][4 * k + 0] + dx * (a->cm[mset][4 * k + 1] + dx * (a->cm[mset][4 * k + 2] + dx * a->cm[mset][4 * k + 3]));
|
||||
yc = a->cc[mset][4 * k + 0] + dx * (a->cc[mset][4 * k + 1] + dx * (a->cc[mset][4 * k + 2] + dx * a->cc[mset][4 * k + 3]));
|
||||
ys = a->cs[mset][4 * k + 0] + dx * (a->cs[mset][4 * k + 1] + dx * (a->cs[mset][4 * k + 2] + dx * a->cs[mset][4 * k + 3]));
|
||||
PRE0 = (1.0 - a->cup[a->count]) * ym * (I * yc - Q * ys) + a->cup[a->count] * PRE0;
|
||||
PRE1 = (1.0 - a->cup[a->count]) * ym * (I * ys + Q * yc) + a->cup[a->count] * PRE1;
|
||||
if (a->count++ == a->ntup)
|
||||
{
|
||||
a->state = RUN;
|
||||
a->count = 0;
|
||||
a->busy = 0; // InterlockedBitTestAndReset (&a->busy, 0);
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
PRE0 = (1.0 - a->cup[a->count]) * PRE0 + a->cup[a->count] * I;
|
||||
PRE1 = (1.0 - a->cup[a->count]) * PRE1 + a->cup[a->count] * Q;
|
||||
if (a->count++ == a->ntup)
|
||||
{
|
||||
a->state = DONE;
|
||||
a->count = 0;
|
||||
a->busy = 0; // InterlockedBitTestAndReset (&a->busy, 0);
|
||||
}
|
||||
break;
|
||||
case DONE:
|
||||
PRE0 = I;
|
||||
PRE1 = Q;
|
||||
break;
|
||||
}
|
||||
a->out[2 * i + 0] = PRE0;
|
||||
a->out[2 * i + 1] = PRE1;
|
||||
// print_iqc_values("iqc.txt", a->state, env, PRE0, PRE1, ym, yc, ys, 1.1);
|
||||
}
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void IQC::setBuffers_iqc (IQC *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void IQC::setSamplerate_iqc (IQC *a, int rate)
|
||||
{
|
||||
decalc_iqc (a);
|
||||
a->rate = rate;
|
||||
calc_iqc (a);
|
||||
}
|
||||
|
||||
void IQC::setSize_iqc (IQC *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void IQC::GetiqcValues (TXA& txa, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC *a;
|
||||
txa.csDSP.lock();
|
||||
a = txa.iqc.p0;
|
||||
memcpy (cm, a->cm[a->cset], a->ints * 4 * sizeof (double));
|
||||
memcpy (cc, a->cc[a->cset], a->ints * 4 * sizeof (double));
|
||||
memcpy (cs, a->cs[a->cset], a->ints * 4 * sizeof (double));
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void IQC::SetiqcValues (TXA& txa, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC *a;
|
||||
txa.csDSP.lock();
|
||||
a = txa.iqc.p0;
|
||||
a->cset = 1 - a->cset;
|
||||
memcpy (a->cm[a->cset], cm, a->ints * 4 * sizeof (double));
|
||||
memcpy (a->cc[a->cset], cc, a->ints * 4 * sizeof (double));
|
||||
memcpy (a->cs[a->cset], cs, a->ints * 4 * sizeof (double));
|
||||
a->state = RUN;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void IQC::SetiqcSwap (TXA& txa, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC *a = txa.iqc.p1;
|
||||
txa.csDSP.lock();
|
||||
a->cset = 1 - a->cset;
|
||||
memcpy (a->cm[a->cset], cm, a->ints * 4 * sizeof (double));
|
||||
memcpy (a->cc[a->cset], cc, a->ints * 4 * sizeof (double));
|
||||
memcpy (a->cs[a->cset], cs, a->ints * 4 * sizeof (double));
|
||||
a->busy = 1; // InterlockedBitTestAndSet (&a->busy, 0);
|
||||
a->state = SWAP;
|
||||
a->count = 0;
|
||||
txa.csDSP.unlock();
|
||||
// while (_InterlockedAnd (&a->busy, 1)) Sleep(1);
|
||||
while (a->busy == 1) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
void IQC::SetiqcStart (TXA& txa, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC *a = txa.iqc.p1;
|
||||
txa.csDSP.lock();
|
||||
a->cset = 0;
|
||||
memcpy (a->cm[a->cset], cm, a->ints * 4 * sizeof (double));
|
||||
memcpy (a->cc[a->cset], cc, a->ints * 4 * sizeof (double));
|
||||
memcpy (a->cs[a->cset], cs, a->ints * 4 * sizeof (double));
|
||||
a->busy = 1; // InterlockedBitTestAndSet (&a->busy, 0);
|
||||
a->state = BEGIN;
|
||||
a->count = 0;
|
||||
txa.csDSP.unlock();
|
||||
txa.iqc.p1->run = 1; //InterlockedBitTestAndSet (&txa.iqc.p1->run, 0);
|
||||
// while (_InterlockedAnd (&a->busy, 1)) Sleep(1);
|
||||
while (a->busy == 1) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
void IQC::SetiqcEnd (TXA& txa)
|
||||
{
|
||||
IQC *a = txa.iqc.p1;
|
||||
txa.csDSP.lock();
|
||||
a->busy = 1; // InterlockedBitTestAndSet (&a->busy, 0);
|
||||
a->state = END;
|
||||
a->count = 0;
|
||||
txa.csDSP.unlock();
|
||||
// while (_InterlockedAnd (&a->busy, 1)) Sleep(1);
|
||||
while (a->busy == 1) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
txa.iqc.p1->run = 0; //InterlockedBitTestAndReset (&txa.iqc.p1->run, 0);
|
||||
}
|
||||
|
||||
void IQC::GetiqcDogCount (TXA& txa, int* count)
|
||||
{
|
||||
IQC *a = txa.iqc.p1;
|
||||
a->dog.cs.lock();
|
||||
*count = a->dog.count;
|
||||
a->dog.cs.unlock();
|
||||
}
|
||||
|
||||
void IQC::SetiqcDogCount (TXA& txa, int count)
|
||||
{
|
||||
IQC *a = txa.iqc.p1;
|
||||
a->dog.cs.lock();
|
||||
a->dog.count = count;
|
||||
a->dog.cs.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
94
wdsp/iqc.hpp
Normal file
94
wdsp/iqc.hpp
Normal file
@ -0,0 +1,94 @@
|
||||
/* iqc.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_iqc_h
|
||||
#define wdsp_iqc_h
|
||||
|
||||
#include <atomic>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API IQC
|
||||
{
|
||||
public:
|
||||
std::atomic<long> run;
|
||||
std::atomic<long> busy;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
int ints;
|
||||
double* t;
|
||||
int cset;
|
||||
double* cm[2];
|
||||
double* cc[2];
|
||||
double* cs[2];
|
||||
double tup;
|
||||
double* cup;
|
||||
int count;
|
||||
int ntup;
|
||||
int state;
|
||||
struct
|
||||
{
|
||||
int spi;
|
||||
int* cpi;
|
||||
int full_ints;
|
||||
int count;
|
||||
QRecursiveMutex cs;
|
||||
} dog;
|
||||
|
||||
static IQC* create_iqc (int run, int size, double* in, double* out, double rate, int ints, double tup, int spi);
|
||||
static void destroy_iqc (IQC *a);
|
||||
static void flush_iqc (IQC *a);
|
||||
static void xiqc (IQC *a);
|
||||
static void setBuffers_iqc (IQC *a, double* in, double* out);
|
||||
static void setSamplerate_iqc (IQC *a, int rate);
|
||||
static void setSize_iqc (IQC *a, int size);
|
||||
// TXA Properties
|
||||
static void GetiqcValues (TXA& txa, double* cm, double* cc, double* cs);
|
||||
static void SetiqcValues (TXA& txa, double* cm, double* cc, double* cs);
|
||||
static void SetiqcSwap (TXA& txa, double* cm, double* cc, double* cs);
|
||||
static void SetiqcStart (TXA& txa, double* cm, double* cc, double* cs);
|
||||
static void SetiqcEnd (TXA& txa);
|
||||
static void GetiqcDogCount (TXA& txa, int* count);
|
||||
static void SetiqcDogCount (TXA& txa, int count);
|
||||
|
||||
private:
|
||||
static void size_iqc (IQC *a);
|
||||
static void desize_iqc (IQC *a);
|
||||
static void calc_iqc (IQC *a);
|
||||
static void decalc_iqc (IQC *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
189
wdsp/lmath.cpp
Normal file
189
wdsp/lmath.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
/* lmath.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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 "lmath.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void LMath::dR (int n, double* r, double* y, double* z)
|
||||
{
|
||||
int i, j, k;
|
||||
double alpha, beta, gamma;
|
||||
memset (z, 0, (n - 1) * sizeof (double)); // work space
|
||||
y[0] = -r[1];
|
||||
alpha = -r[1];
|
||||
beta = 1.0;
|
||||
for (k = 0; k < n - 1; k++)
|
||||
{
|
||||
beta *= 1.0 - alpha * alpha;
|
||||
gamma = 0.0;
|
||||
for (i = k + 1, j = 0; i > 0; i--, j++)
|
||||
gamma += r[i] * y[j];
|
||||
alpha = - (r[k + 2] + gamma) / beta;
|
||||
for (i = 0, j = k; i <= k; i++, j--)
|
||||
z[i] = y[i] + alpha * y[j];
|
||||
memcpy (y, z, (k + 1) * sizeof (double));
|
||||
y[k + 1] = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
void LMath::trI (
|
||||
int n,
|
||||
double* r,
|
||||
double* B,
|
||||
double* y,
|
||||
double* v,
|
||||
double* dR_z
|
||||
)
|
||||
{
|
||||
int i, j, ni, nj;
|
||||
double gamma, t, scale, b;
|
||||
memset (y, 0, (n - 1) * sizeof (double)); // work space
|
||||
memset (v, 0, (n - 1) * sizeof (double)); // work space
|
||||
scale = 1.0 / r[0];
|
||||
for (i = 0; i < n; i++)
|
||||
r[i] *= scale;
|
||||
dR(n - 1, r, y, dR_z);
|
||||
|
||||
t = 0.0;
|
||||
for (i = 0; i < n - 1; i++)
|
||||
t += r[i + 1] * y[i];
|
||||
gamma = 1.0 / (1.0 + t);
|
||||
for (i = 0, j = n - 2; i < n - 1; i++, j--)
|
||||
v[i] = gamma * y[j];
|
||||
B[0] = gamma;
|
||||
for (i = 1, j = n - 2; i < n; i++, j--)
|
||||
B[i] = v[j];
|
||||
for (i = 1; i <= (n - 1) / 2; i++)
|
||||
for (j = i; j < n - i; j++)
|
||||
B[i * n + j] = B[(i - 1) * n + (j - 1)] + (v[n - j - 1] * v[n - i - 1] - v[i - 1] * v[j - 1]) / gamma;
|
||||
for (i = 0; i <= (n - 1)/2; i++)
|
||||
for (j = i; j < n - i; j++)
|
||||
{
|
||||
b = B[i * n + j] *= scale;
|
||||
B[j * n + i] = b;
|
||||
ni = n - i - 1;
|
||||
nj = n - j - 1;
|
||||
B[ni * n + nj] = b;
|
||||
B[nj * n + ni] = b;
|
||||
}
|
||||
}
|
||||
|
||||
void LMath::asolve(int xsize, int asize, double* x, double* a, double* r, double* z)
|
||||
{
|
||||
int i, j, k;
|
||||
double beta, alpha, t;
|
||||
memset(r, 0, (asize + 1) * sizeof(double)); // work space
|
||||
memset(z, 0, (asize + 1) * sizeof(double)); // work space
|
||||
for (i = 0; i <= asize; i++)
|
||||
{
|
||||
for (j = 0; j < xsize; j++)
|
||||
r[i] += x[j] * x[j - i];
|
||||
}
|
||||
z[0] = 1.0;
|
||||
beta = r[0];
|
||||
for (k = 0; k < asize; k++)
|
||||
{
|
||||
alpha = 0.0;
|
||||
for (j = 0; j <= k; j++)
|
||||
alpha -= z[j] * r[k + 1 - j];
|
||||
alpha /= beta;
|
||||
for (i = 0; i <= (k + 1) / 2; i++)
|
||||
{
|
||||
t = z[k + 1 - i] + alpha * z[i];
|
||||
z[i] = z[i] + alpha * z[k + 1 - i];
|
||||
z[k + 1 - i] = t;
|
||||
}
|
||||
beta *= 1.0 - alpha * alpha;
|
||||
}
|
||||
for (i = 0; i < asize; i++)
|
||||
{
|
||||
a[i] = - z[i + 1];
|
||||
if (a[i] != a[i]) a[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void LMath::median (int n, double* a, double* med)
|
||||
{
|
||||
int S0, S1, i, j, m, k;
|
||||
double x, t;
|
||||
S0 = 0;
|
||||
S1 = n - 1;
|
||||
k = n / 2;
|
||||
while (S1 > S0 + 1)
|
||||
{
|
||||
m = (S0 + S1) / 2;
|
||||
t = a[m];
|
||||
a[m] = a[S0 + 1];
|
||||
a[S0 + 1] = t;
|
||||
if (a[S0] > a[S1])
|
||||
{
|
||||
t = a[S0];
|
||||
a[S0] = a[S1];
|
||||
a[S1] = t;
|
||||
}
|
||||
if (a[S0 + 1] > a[S1])
|
||||
{
|
||||
t = a[S0 + 1];
|
||||
a[S0 + 1] = a[S1];
|
||||
a[S1] = t;
|
||||
}
|
||||
if (a[S0] > a[S0 + 1])
|
||||
{
|
||||
t = a[S0];
|
||||
a[S0] = a[S0 + 1];
|
||||
a[S0 + 1] = t;
|
||||
}
|
||||
i = S0 + 1;
|
||||
j = S1;
|
||||
x = a[S0 + 1];
|
||||
do i++; while (a[i] < x);
|
||||
do j--; while (a[j] > x);
|
||||
while (j >= i)
|
||||
{
|
||||
t = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = t;
|
||||
do i++; while (a[i] < x);
|
||||
do j--; while (a[j] > x);
|
||||
}
|
||||
a[S0 + 1] = a[j];
|
||||
a[j] = x;
|
||||
if (j >= k) S1 = j - 1;
|
||||
if (j <= k) S0 = i;
|
||||
}
|
||||
if (S1 == S0 + 1 && a[S1] < a[S0])
|
||||
{
|
||||
t = a[S0];
|
||||
a[S0] = a[S1];
|
||||
a[S1] = t;
|
||||
}
|
||||
*med = a[k];
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
53
wdsp/lmath.hpp
Normal file
53
wdsp/lmath.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
/* lmath.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_lmath
|
||||
#define wdsp_lmath
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API LMath {
|
||||
public:
|
||||
static void dR (int n, double* r, double* y, double* z);
|
||||
static void trI (
|
||||
int n,
|
||||
double* r,
|
||||
double* B,
|
||||
double* y,
|
||||
double* v,
|
||||
double* dR_z
|
||||
);
|
||||
static void asolve(int xsize, int asize, double* x, double* a, double* r, double* z);
|
||||
static void median(int n, double* a, double* med);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
|
||||
63
wdsp/make_calculus.cpp
Normal file
63
wdsp/make_calculus.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* make_calculus
|
||||
*
|
||||
* This program reads the contents of the binary WDSP file "calculus"
|
||||
* and dumps the data as two arrays of floating-point numbers
|
||||
*
|
||||
* The output is intended to be part of the file "calculus.c" which
|
||||
* initializes these arrays (static data) for use with "memcpy"
|
||||
* in emnr.c.
|
||||
*
|
||||
* Should the WDSP file "calculus" be changed, "calculus.c" should
|
||||
* be re-generated using this program.
|
||||
*
|
||||
* return values of main()
|
||||
*
|
||||
* 0 all OK
|
||||
* -1 sizeof(double) is not 8
|
||||
* -2 error opening file "calculus"
|
||||
* -3 read error
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main() {
|
||||
int fd;
|
||||
int i,j;
|
||||
double d;
|
||||
|
||||
if (sizeof(double) != 8) {
|
||||
printf("Data type DOUBLE is not 8-byte. Please check!\n");
|
||||
return -1;
|
||||
}
|
||||
fd=open ("calculus", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("Could not open file 'calculus'\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (j=0; j<2; j++) {
|
||||
switch (j) {
|
||||
case 0:
|
||||
printf("double GG[241*241]={\n");
|
||||
break;
|
||||
case 1:
|
||||
printf("double GGS[241*241]={\n");
|
||||
break;
|
||||
}
|
||||
for (i=0; i< 241*241; i++) {
|
||||
if (read(fd, &d, 8) != 8) {
|
||||
printf("READ ERROR\n");
|
||||
return -3;
|
||||
}
|
||||
if (i == 241*241 -1) {
|
||||
printf("%30.25f};\n", d);
|
||||
} else {
|
||||
printf("%30.25f,\n", d);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
101
wdsp/make_interface.cpp
Normal file
101
wdsp/make_interface.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
The purpose of this file is to extract interfaces from the WDSP source code.
|
||||
The interfaces have the following form:
|
||||
|
||||
PORT blabla
|
||||
firstline
|
||||
secondline
|
||||
{
|
||||
|
||||
where there may be an arbitrary number of lines between the line
|
||||
containing "PORT" and the line starting with "{". This has to be
|
||||
converted to
|
||||
|
||||
extern blabla firstline
|
||||
secondline;
|
||||
|
||||
That is, the first line is pre-pended by "extern", and the last line is closed
|
||||
with a semicolon. Comments starting with '//' are omitted, and lines starting
|
||||
with '//' are ignored.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void trimm(char *line, size_t maxlen);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
FILE *infile;
|
||||
int i;
|
||||
int first_in_file;
|
||||
int first_in_decl;
|
||||
char line[1000];
|
||||
size_t linesize=999;
|
||||
char *buffer=line;
|
||||
|
||||
for (i=1; i<argc; i++) {
|
||||
infile=fopen(argv[i],"r");
|
||||
if (infile == NULL) continue;
|
||||
first_in_file=1;
|
||||
for (;;) {
|
||||
if (getline(&buffer, &linesize, infile) < 0) break;
|
||||
trimm(line, linesize);
|
||||
if (strncmp(line,"PORT", 4) != 0) continue;
|
||||
// found an interface
|
||||
if (first_in_file) {
|
||||
printf("\n//\n// Interfaces from %s\n//\n\n", argv[i]);
|
||||
first_in_file=0;
|
||||
}
|
||||
if (strlen(line) >4) {
|
||||
printf("extern %s ", line+4);
|
||||
} else {
|
||||
printf("extern ");
|
||||
}
|
||||
first_in_decl=1;
|
||||
|
||||
for (;;) {
|
||||
if (getline(&buffer, &linesize, infile) < 0) {
|
||||
fprintf(stderr,"! Found a PORT but found EOF while scanning interface.\n");
|
||||
return 8;
|
||||
}
|
||||
trimm(line, linesize);
|
||||
if (line[0] == 0) continue;
|
||||
if (line[0] == '{') {
|
||||
printf(";\n");
|
||||
break;
|
||||
} else {
|
||||
if (first_in_decl) {
|
||||
printf("%s", line);
|
||||
first_in_decl=0;
|
||||
} else {
|
||||
printf("\n%s", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(infile);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void trimm(char *line, size_t maxlen) {
|
||||
int len;
|
||||
|
||||
//
|
||||
// Remove comments starting with '//'
|
||||
//
|
||||
len=strnlen(line,maxlen);
|
||||
for (int i=0; i< len-1; i++) {
|
||||
if (line[i] == '/' && line[i+1] == '/') line[i]=0;
|
||||
}
|
||||
|
||||
//
|
||||
// Remove trailing white space and newlines
|
||||
//
|
||||
len=strnlen(line,maxlen);
|
||||
line[len--]=0;
|
||||
while (len >= 0 && (line[len] == ' ' || line[len] == '\t' || line[len]== '\n')) line[len--]=0;
|
||||
}
|
||||
178
wdsp/meter.cpp
Normal file
178
wdsp/meter.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
/* meter.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "meterlog10.hpp"
|
||||
#include "meter.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void METER::calc_meter (METER *a)
|
||||
{
|
||||
a->mult_average = exp(-1.0 / (a->rate * a->tau_average));
|
||||
a->mult_peak = exp(-1.0 / (a->rate * a->tau_peak_decay));
|
||||
flush_meter(a);
|
||||
}
|
||||
|
||||
METER* METER::create_meter (
|
||||
int run,
|
||||
int* prun,
|
||||
int size,
|
||||
double* buff,
|
||||
int rate,
|
||||
double tau_av,
|
||||
double tau_decay,
|
||||
double* result,
|
||||
QRecursiveMutex** pmtupdate,
|
||||
int enum_av,
|
||||
int enum_pk,
|
||||
int enum_gain,
|
||||
double* pgain
|
||||
)
|
||||
{
|
||||
METER *a = new METER;
|
||||
a->run = run;
|
||||
a->prun = prun;
|
||||
a->size = size;
|
||||
a->buff = buff;
|
||||
a->rate = (double)rate;
|
||||
a->tau_average = tau_av;
|
||||
a->tau_peak_decay = tau_decay;
|
||||
a->result = result;
|
||||
a->enum_av = enum_av;
|
||||
a->enum_pk = enum_pk;
|
||||
a->enum_gain = enum_gain;
|
||||
a->pgain = pgain;
|
||||
calc_meter(a);
|
||||
pmtupdate[enum_av] = &a->mtupdate;
|
||||
pmtupdate[enum_pk] = &a->mtupdate;
|
||||
pmtupdate[enum_gain] = &a->mtupdate;
|
||||
return a;
|
||||
}
|
||||
|
||||
void METER::destroy_meter (METER *a)
|
||||
{
|
||||
delete a;
|
||||
}
|
||||
|
||||
void METER::flush_meter (METER *a)
|
||||
{
|
||||
a->avg = 0.0;
|
||||
a->peak = 0.0;
|
||||
a->result[a->enum_av] = -400.0;
|
||||
a->result[a->enum_pk] = -400.0;
|
||||
if ((a->pgain != 0) && (a->enum_gain >= 0))
|
||||
a->result[a->enum_gain] = -400.0;
|
||||
}
|
||||
|
||||
void METER::xmeter (METER *a)
|
||||
{
|
||||
int srun;
|
||||
a->mtupdate.lock();
|
||||
if (a->prun != 0)
|
||||
srun = *(a->prun);
|
||||
else
|
||||
srun = 1;
|
||||
if (a->run && srun)
|
||||
{
|
||||
int i;
|
||||
double smag;
|
||||
double np = 0.0;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
smag = a->buff[2 * i + 0] * a->buff[2 * i + 0] + a->buff[2 * i + 1] * a->buff[2 * i + 1];
|
||||
a->avg = a->avg * a->mult_average + (1.0 - a->mult_average) * smag;
|
||||
a->peak *= a->mult_peak;
|
||||
if (smag > np) np = smag;
|
||||
}
|
||||
if (np > a->peak) a->peak = np;
|
||||
a->result[a->enum_av] = 10.0 * MemLog::mlog10 (a->avg + 1.0e-40);
|
||||
a->result[a->enum_pk] = 10.0 * MemLog::mlog10 (a->peak + 1.0e-40);
|
||||
if ((a->pgain != 0) && (a->enum_gain >= 0))
|
||||
a->result[a->enum_gain] = 20.0 * MemLog::mlog10 (*a->pgain + 1.0e-40);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a->enum_av >= 0) a->result[a->enum_av] = - 400.0;
|
||||
if (a->enum_pk >= 0) a->result[a->enum_pk] = - 400.0;
|
||||
if (a->enum_gain >= 0) a->result[a->enum_gain] = + 0.0;
|
||||
}
|
||||
a->mtupdate.unlock();
|
||||
}
|
||||
|
||||
void METER::setBuffers_meter (METER *a, double* in)
|
||||
{
|
||||
a->buff = in;
|
||||
}
|
||||
|
||||
void METER::setSamplerate_meter (METER *a, int rate)
|
||||
{
|
||||
a->rate = rate;
|
||||
calc_meter(a);
|
||||
}
|
||||
|
||||
void METER::setSize_meter (METER *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_meter (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// PORT
|
||||
// double GetRXAMeter (int channel, int mt)
|
||||
// {
|
||||
// double val;
|
||||
// CRITICAL_SECTION* a = rxa[channel].pmtupdate[mt];
|
||||
// EnterCriticalSection (a);
|
||||
// val = rxa[channel].meter[mt];
|
||||
// LeaveCriticalSection (a);
|
||||
// return val;
|
||||
// }
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// PORT
|
||||
// double GetTXAMeter (int channel, int mt)
|
||||
// {
|
||||
// double val;
|
||||
// CRITICAL_SECTION* a = txa[channel].pmtupdate[mt];
|
||||
// EnterCriticalSection (a);
|
||||
// val = txa[channel].meter[mt];
|
||||
// LeaveCriticalSection (a);
|
||||
// return val;
|
||||
// }
|
||||
|
||||
} // namespace WDSP
|
||||
95
wdsp/meter.hpp
Normal file
95
wdsp/meter.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
/* meter.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _meter_h
|
||||
#define _meter_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API METER
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int* prun;
|
||||
int size;
|
||||
double* buff;
|
||||
double rate;
|
||||
double tau_average;
|
||||
double tau_peak_decay;
|
||||
double mult_average;
|
||||
double mult_peak;
|
||||
double* result;
|
||||
int enum_av;
|
||||
int enum_pk;
|
||||
int enum_gain;
|
||||
double* pgain;
|
||||
double avg;
|
||||
double peak;
|
||||
QRecursiveMutex mtupdate;
|
||||
|
||||
static METER* create_meter (
|
||||
int run,
|
||||
int* prun,
|
||||
int size,
|
||||
double* buff,
|
||||
int rate,
|
||||
double tau_av,
|
||||
double tau_decay,
|
||||
double* result,
|
||||
QRecursiveMutex** pmtupdate,
|
||||
int enum_av,
|
||||
int enum_pk,
|
||||
int enum_gain,
|
||||
double* pgain
|
||||
);
|
||||
static void destroy_meter (METER *a);
|
||||
static void flush_meter (METER *a);
|
||||
static void xmeter (METER *a);
|
||||
static void setBuffers_meter (METER *a, double* in);
|
||||
static void setSamplerate_meter (METER *a, int rate);
|
||||
static void setSize_meter (METER *a, int size);
|
||||
|
||||
private:
|
||||
static void calc_meter (METER *a);
|
||||
};
|
||||
|
||||
|
||||
// RXA Properties
|
||||
|
||||
// __declspec (dllexport) double GetRXAMeter (int channel, int mt);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
// __declspec (dllexport) double GetTXAMeter (int channel, int mt);
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
560
wdsp/meterlog10.cpp
Normal file
560
wdsp/meterlog10.cpp
Normal file
@ -0,0 +1,560 @@
|
||||
/* meterlog10.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "meterlog10.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
const int MemLog::mbits = 11;
|
||||
const int MemLog::mmask = 2047;
|
||||
const double MemLog::mconv = 0.301029995663981;
|
||||
const double MemLog::mtable[2048] = {
|
||||
0.0000000000000000e+000, 7.0426901124664325e-004, 1.4081943928083889e-003, 2.1117764798519820e-003,
|
||||
2.8150156070540383e-003, 3.5179121086019987e-003, 4.2204663181950848e-003, 4.9226785690452447e-003,
|
||||
5.6245491938781075e-003, 6.3260785249339207e-003, 7.0272668939685033e-003, 7.7281146322541816e-003,
|
||||
8.4286220705807290e-003, 9.1287895392563011e-003, 9.8286173681083767e-003, 1.0528105886484680e-002,
|
||||
1.1227255423254120e-002, 1.1926066306807714e-002, 1.2624538865059513e-002, 1.3322673425447519e-002,
|
||||
1.4020470314934629e-002, 1.4717929860009516e-002, 1.5415052386687583e-002, 1.6111838220511852e-002,
|
||||
1.6808287686553888e-002, 1.7504401109414709e-002, 1.8200178813225682e-002, 1.8895621121649452e-002,
|
||||
1.9590728357880813e-002, 2.0285500844647641e-002, 2.0979938904211771e-002, 2.1674042858369907e-002,
|
||||
2.2367813028454510e-002, 2.3061249735334684e-002, 2.3754353299417087e-002, 2.4447124040646806e-002,
|
||||
2.5139562278508228e-002, 2.5831668332025957e-002, 2.6523442519765676e-002, 2.7214885159835025e-002,
|
||||
2.7905996569884486e-002, 2.8596777067108246e-002, 2.9287226968245095e-002, 2.9977346589579280e-002,
|
||||
3.0667136246941371e-002, 3.1356596255709142e-002, 3.2045726930808413e-002, 3.2734528586713947e-002,
|
||||
3.3423001537450280e-002, 3.4111146096592597e-002, 3.4798962577267598e-002, 3.5486451292154303e-002,
|
||||
3.6173612553484981e-002, 3.6860446673045943e-002, 3.7546953962178407e-002, 3.8233134731779347e-002,
|
||||
3.8918989292302350e-002, 3.9604517953758425e-002, 4.0289721025716888e-002, 4.0974598817306154e-002,
|
||||
4.1659151637214620e-002, 4.2343379793691460e-002, 4.3027283594547480e-002, 4.3710863347155952e-002,
|
||||
4.4394119358453436e-002, 4.5077051934940590e-002, 4.5759661382683010e-002, 4.6441948007312085e-002,
|
||||
4.7123912114025744e-002, 4.7805554007589353e-002, 4.8486873992336470e-002, 4.9167872372169713e-002,
|
||||
4.9848549450561525e-002, 5.0528905530555022e-002, 5.1208940914764793e-002, 5.1888655905377674e-002,
|
||||
5.2568050804153624e-002, 5.3247125912426453e-002, 5.3925881531104676e-002, 5.4604317960672298e-002,
|
||||
5.5282435501189602e-002, 5.5960234452293950e-002, 5.6637715113200586e-002, 5.7314877782703437e-002,
|
||||
5.7991722759175850e-002, 5.8668250340571465e-002, 5.9344460824424933e-002, 6.0020354507852730e-002,
|
||||
6.0695931687553932e-002, 6.1371192659811009e-002, 6.2046137720490599e-002, 6.2720767165044267e-002,
|
||||
6.3395081288509306e-002, 6.4069080385509486e-002, 6.4742764750255880e-002, 6.5416134676547555e-002,
|
||||
6.6089190457772423e-002, 6.6761932386907946e-002, 6.7434360756521899e-002, 6.8106475858773191e-002,
|
||||
6.8778277985412611e-002, 6.9449767427783499e-002, 7.0120944476822628e-002, 7.0791809423060917e-002,
|
||||
7.1462362556624137e-002, 7.2132604167233730e-002, 7.2802534544207528e-002, 7.3472153976460494e-002,
|
||||
7.4141462752505466e-002, 7.4810461160453992e-002, 7.5479149488016914e-002, 7.6147528022505254e-002,
|
||||
7.6815597050830894e-002, 7.7483356859507274e-002, 7.8150807734650246e-002, 7.8817949961978662e-002,
|
||||
7.9484783826815258e-002, 8.0151309614087257e-002, 8.0817527608327167e-002, 8.1483438093673519e-002,
|
||||
8.2149041353871563e-002, 8.2814337672273974e-002, 8.3479327331841671e-002, 8.4144010615144385e-002,
|
||||
8.4808387804361535e-002, 8.5472459181282878e-002, 8.6136225027309166e-002, 8.6799685623452999e-002,
|
||||
8.7462841250339415e-002, 8.8125692188206650e-002, 8.8788238716906873e-002, 8.9450481115906866e-002,
|
||||
9.0112419664288707e-002, 9.0774054640750543e-002, 9.1435386323607218e-002, 9.2096414990791034e-002,
|
||||
9.2757140919852446e-002, 9.3417564387960714e-002, 9.4077685671904637e-002, 9.4737505048093279e-002,
|
||||
9.5397022792556560e-002, 9.6056239180946093e-002, 9.6715154488535765e-002, 9.7373768990222445e-002,
|
||||
9.8032082960526720e-002, 9.8690096673593519e-002, 9.9347810403192877e-002, 1.0000522442272053e-001,
|
||||
1.0066233900519864e-001, 1.0131915442327648e-001, 1.0197567094923112e-001, 1.0263188885496805e-001,
|
||||
1.0328780841202195e-001, 1.0394342989155728e-001, 1.0459875356436894e-001, 1.0525377970088309e-001,
|
||||
1.0590850857115762e-001, 1.0656294044488293e-001, 1.0721707559138261e-001, 1.0787091427961401e-001,
|
||||
1.0852445677816905e-001, 1.0917770335527474e-001, 1.0983065427879392e-001, 1.1048330981622587e-001,
|
||||
1.1113567023470704e-001, 1.1178773580101162e-001, 1.1243950678155225e-001, 1.1309098344238065e-001,
|
||||
1.1374216604918833e-001, 1.1439305486730712e-001, 1.1504365016170993e-001, 1.1569395219701137e-001,
|
||||
1.1634396123746831e-001, 1.1699367754698069e-001, 1.1764310138909200e-001, 1.1829223302699000e-001,
|
||||
1.1894107272350742e-001, 1.1958962074112245e-001, 1.2023787734195947e-001, 1.2088584278778969e-001,
|
||||
1.2153351734003176e-001, 1.2218090125975241e-001, 1.2282799480766708e-001, 1.2347479824414055e-001,
|
||||
1.2412131182918758e-001, 1.2476753582247349e-001, 1.2541347048331486e-001, 1.2605911607068010e-001,
|
||||
1.2670447284319009e-001, 1.2734954105911880e-001, 1.2799432097639396e-001, 1.2863881285259751e-001,
|
||||
1.2928301694496647e-001, 1.2992693351039331e-001, 1.3057056280542675e-001, 1.3121390508627229e-001,
|
||||
1.3185696060879285e-001, 1.3249972962850928e-001, 1.3314221240060117e-001, 1.3378440917990725e-001,
|
||||
1.3442632022092610e-001, 1.3506794577781681e-001, 1.3570928610439942e-001, 1.3635034145415567e-001,
|
||||
1.3699111208022957e-001, 1.3763159823542795e-001, 1.3827180017222110e-001, 1.3891171814274328e-001,
|
||||
1.3955135239879354e-001, 1.4019070319183608e-001, 1.4082977077300082e-001, 1.4146855539308437e-001,
|
||||
1.4210705730255005e-001, 1.4274527675152901e-001, 1.4338321398982046e-001, 1.4402086926689242e-001,
|
||||
1.4465824283188231e-001, 1.4529533493359742e-001, 1.4593214582051564e-001, 1.4656867574078589e-001,
|
||||
1.4720492494222884e-001, 1.4784089367233738e-001, 1.4847658217827733e-001, 1.4911199070688783e-001,
|
||||
1.4974711950468206e-001, 1.5038196881784779e-001, 1.5101653889224789e-001, 1.5165082997342097e-001,
|
||||
1.5228484230658190e-001, 1.5291857613662249e-001, 1.5355203170811183e-001, 1.5418520926529714e-001,
|
||||
1.5481810905210403e-001, 1.5545073131213735e-001, 1.5608307628868168e-001, 1.5671514422470162e-001,
|
||||
1.5734693536284278e-001, 1.5797844994543206e-001, 1.5860968821447821e-001, 1.5924065041167257e-001,
|
||||
1.5987133677838941e-001, 1.6050174755568664e-001, 1.6113188298430628e-001, 1.6176174330467505e-001,
|
||||
1.6239132875690487e-001, 1.6302063958079349e-001, 1.6364967601582500e-001, 1.6427843830117031e-001,
|
||||
1.6490692667568779e-001, 1.6553514137792383e-001, 1.6616308264611326e-001, 1.6679075071818000e-001,
|
||||
1.6741814583173756e-001, 1.6804526822408961e-001, 1.6867211813223043e-001, 1.6929869579284559e-001,
|
||||
1.6992500144231237e-001, 1.7055103531670032e-001, 1.7117679765177185e-001, 1.7180228868298272e-001,
|
||||
1.7242750864548248e-001, 1.7305245777411524e-001, 1.7367713630341988e-001, 1.7430154446763094e-001,
|
||||
1.7492568250067883e-001, 1.7554955063619046e-001, 1.7617314910748991e-001, 1.7679647814759875e-001,
|
||||
1.7741953798923660e-001, 1.7804232886482183e-001, 1.7866485100647178e-001, 1.7928710464600359e-001,
|
||||
1.7990909001493446e-001, 1.8053080734448240e-001, 1.8115225686556649e-001, 1.8177343880880761e-001,
|
||||
1.8239435340452886e-001, 1.8301500088275607e-001, 1.8363538147321837e-001, 1.8425549540534858e-001,
|
||||
1.8487534290828386e-001, 1.8549492421086611e-001, 1.8611423954164258e-001, 1.8673328912886625e-001,
|
||||
1.8735207320049643e-001, 1.8797059198419924e-001, 1.8858884570734810e-001, 1.8920683459702425e-001,
|
||||
1.8982455888001723e-001, 1.9044201878282538e-001, 1.9105921453165634e-001, 1.9167614635242758e-001,
|
||||
1.9229281447076688e-001, 1.9290921911201278e-001, 1.9352536050121508e-001, 1.9414123886313550e-001,
|
||||
1.9475685442224788e-001, 1.9537220740273889e-001, 1.9598729802850845e-001, 1.9660212652317025e-001,
|
||||
1.9721669311005219e-001, 1.9783099801219684e-001, 1.9844504145236205e-001, 1.9905882365302136e-001,
|
||||
1.9967234483636440e-001, 2.0028560522429753e-001, 2.0089860503844420e-001, 2.0151134450014555e-001,
|
||||
2.0212382383046071e-001, 2.0273604325016748e-001, 2.0334800297976266e-001, 2.0395970323946255e-001,
|
||||
2.0457114424920361e-001, 2.0518232622864258e-001, 2.0579324939715726e-001, 2.0640391397384683e-001,
|
||||
2.0701432017753244e-001, 2.0762446822675754e-001, 2.0823435833978840e-001, 2.0884399073461471e-001,
|
||||
2.0945336562894978e-001, 2.1006248324023125e-001, 2.1067134378562147e-001, 2.1127994748200787e-001,
|
||||
2.1188829454600366e-001, 2.1249638519394801e-001, 2.1310421964190671e-001, 2.1371179810567254e-001,
|
||||
2.1431912080076582e-001, 2.1492618794243468e-001, 2.1553299974565579e-001, 2.1613955642513461e-001,
|
||||
2.1674585819530584e-001, 2.1735190527033405e-001, 2.1795769786411398e-001, 2.1856323619027099e-001,
|
||||
2.1916852046216157e-001, 2.1977355089287390e-001, 2.2037832769522800e-001, 2.2098285108177657e-001,
|
||||
2.2158712126480504e-001, 2.2219113845633220e-001, 2.2279490286811091e-001, 2.2339841471162797e-001,
|
||||
2.2400167419810504e-001, 2.2460468153849889e-001, 2.2520743694350198e-001, 2.2580994062354257e-001,
|
||||
2.2641219278878566e-001, 2.2701419364913295e-001, 2.2761594341422362e-001, 2.2821744229343452e-001,
|
||||
2.2881869049588088e-001, 2.2941968823041642e-001, 2.3002043570563410e-001, 2.3062093312986631e-001,
|
||||
2.3122118071118541e-001, 2.3182117865740429e-001, 2.3242092717607646e-001, 2.3302042647449686e-001,
|
||||
2.3361967675970208e-001, 2.3421867823847067e-001, 2.3481743111732398e-001, 2.3541593560252616e-001,
|
||||
2.3601419190008480e-001, 2.3661220021575136e-001, 2.3720996075502146e-001, 2.3780747372313552e-001,
|
||||
2.3840473932507891e-001, 2.3900175776558261e-001, 2.3959852924912356e-001, 2.4019505397992494e-001,
|
||||
2.4079133216195686e-001, 2.4138736399893651e-001, 2.4198314969432871e-001, 2.4257868945134642e-001,
|
||||
2.4317398347295091e-001, 2.4376903196185237e-001, 2.4436383512051027e-001, 2.4495839315113374e-001,
|
||||
2.4555270625568210e-001, 2.4614677463586504e-001, 2.4674059849314325e-001, 2.4733417802872884e-001,
|
||||
2.4792751344358549e-001, 2.4852060493842920e-001, 2.4911345271372837e-001, 2.4970605696970449e-001,
|
||||
2.5029841790633245e-001, 2.5089053572334064e-001, 2.5148241062021204e-001, 2.5207404279618389e-001,
|
||||
2.5266543245024864e-001, 2.5325657978115390e-001, 2.5384748498740323e-001, 2.5443814826725630e-001,
|
||||
2.5502856981872951e-001, 2.5561874983959609e-001, 2.5620868852738665e-001, 2.5679838607938965e-001,
|
||||
2.5738784269265180e-001, 2.5797705856397818e-001, 2.5856603388993293e-001, 2.5915476886683958e-001,
|
||||
2.5974326369078155e-001, 2.6033151855760200e-001, 2.6091953366290499e-001, 2.6150730920205539e-001,
|
||||
2.6209484537017941e-001, 2.6268214236216481e-001, 2.6326920037266166e-001, 2.6385601959608235e-001,
|
||||
2.6444260022660221e-001, 2.6502894245815983e-001, 2.6561504648445733e-001, 2.6620091249896100e-001,
|
||||
2.6678654069490132e-001, 2.6737193126527387e-001, 2.6795708440283911e-001, 2.6854200030012310e-001,
|
||||
2.6912667914941790e-001, 2.6971112114278190e-001, 2.7029532647204002e-001, 2.7087929532878435e-001,
|
||||
2.7146302790437460e-001, 2.7204652438993787e-001, 2.7262978497636975e-001, 2.7321280985433433e-001,
|
||||
2.7379559921426466e-001, 2.7437815324636305e-001, 2.7496047214060154e-001, 2.7554255608672223e-001,
|
||||
2.7612440527423759e-001, 2.7670601989243088e-001, 2.7728740013035663e-001, 2.7786854617684087e-001,
|
||||
2.7844945822048139e-001, 2.7903013644964852e-001, 2.7961058105248499e-001, 2.8019079221690657e-001,
|
||||
2.8077077013060253e-001, 2.8135051498103575e-001, 2.8193002695544328e-001, 2.8250930624083653e-001,
|
||||
2.8308835302400187e-001, 2.8366716749150073e-001, 2.8424574982967005e-001, 2.8482410022462290e-001,
|
||||
2.8540221886224837e-001, 2.8598010592821227e-001, 2.8655776160795737e-001, 2.8713518608670380e-001,
|
||||
2.8771237954944939e-001, 2.8828934218096997e-001, 2.8886607416581983e-001, 2.8944257568833187e-001,
|
||||
2.9001884693261837e-001, 2.9059488808257078e-001, 2.9117069932186052e-001, 2.9174628083393922e-001,
|
||||
2.9232163280203893e-001, 2.9289675540917259e-001, 2.9347164883813431e-001, 2.9404631327149994e-001,
|
||||
2.9462074889162698e-001, 2.9519495588065547e-001, 2.9576893442050778e-001, 2.9634268469288944e-001,
|
||||
2.9691620687928921e-001, 2.9748950116097944e-001, 2.9806256771901651e-001, 2.9863540673424116e-001,
|
||||
2.9920801838727878e-001, 2.9978040285853974e-001, 3.0035256032821983e-001, 3.0092449097630042e-001,
|
||||
3.0149619498254898e-001, 3.0206767252651939e-001, 3.0263892378755225e-001, 3.0320994894477510e-001,
|
||||
3.0378074817710293e-001, 3.0435132166323847e-001, 3.0492166958167249e-001, 3.0549179211068411e-001,
|
||||
3.0606168942834128e-001, 3.0663136171250094e-001, 3.0720080914080944e-001, 3.0777003189070284e-001,
|
||||
3.0833903013940728e-001, 3.0890780406393920e-001, 3.0947635384110589e-001, 3.1004467964750571e-001,
|
||||
3.1061278165952821e-001, 3.1118066005335476e-001, 3.1174831500495892e-001, 3.1231574669010626e-001,
|
||||
3.1288295528435539e-001, 3.1344994096305762e-001, 3.1401670390135794e-001, 3.1458324427419465e-001,
|
||||
3.1514956225630036e-001, 3.1571565802220181e-001, 3.1628153174622031e-001, 3.1684718360247227e-001,
|
||||
3.1741261376486940e-001, 3.1797782240711886e-001, 3.1854280970272381e-001, 3.1910757582498367e-001,
|
||||
3.1967212094699443e-001, 3.2023644524164885e-001, 3.2080054888163700e-001, 3.2136443203944637e-001,
|
||||
3.2192809488736235e-001, 3.2249153759746840e-001, 3.2305476034164660e-001, 3.2361776329157749e-001,
|
||||
3.2418054661874102e-001, 3.2474311049441640e-001, 3.2530545508968250e-001, 3.2586758057541831e-001,
|
||||
3.2642948712230313e-001, 3.2699117490081686e-001, 3.2755264408124046e-001, 3.2811389483365605e-001,
|
||||
3.2867492732794734e-001, 3.2923574173379999e-001, 3.2979633822070181e-001, 3.3035671695794311e-001,
|
||||
3.3091687811461695e-001, 3.3147682185961974e-001, 3.3203654836165097e-001, 3.3259605778921408e-001,
|
||||
3.3315535031061649e-001, 3.3371442609396990e-001, 3.3427328530719069e-001, 3.3483192811800017e-001,
|
||||
3.3539035469392492e-001, 3.3594856520229699e-001, 3.3650655981025435e-001, 3.3706433868474101e-001,
|
||||
3.3762190199250752e-001, 3.3817924990011117e-001, 3.3873638257391619e-001, 3.3929330018009435e-001,
|
||||
3.3985000288462475e-001, 3.4040649085329461e-001, 3.4096276425169947e-001, 3.4151882324524319e-001,
|
||||
3.4207466799913855e-001, 3.4263029867840739e-001, 3.4318571544788107e-001, 3.4374091847220056e-001,
|
||||
3.4429590791581688e-001, 3.4485068394299118e-001, 3.4540524671779538e-001, 3.4595959640411222e-001,
|
||||
3.4651373316563550e-001, 3.4706765716587057e-001, 3.4762136856813453e-001, 3.4817486753555638e-001,
|
||||
3.4872815423107756e-001, 3.4928122881745205e-001, 3.4983409145724681e-001, 3.5038674231284189e-001,
|
||||
3.5093918154643078e-001, 3.5149140932002076e-001, 3.5204342579543318e-001, 3.5259523113430374e-001,
|
||||
3.5314682549808257e-001, 3.5369820904803478e-001, 3.5424938194524075e-001, 3.5480034435059621e-001,
|
||||
3.5535109642481261e-001, 3.5590163832841742e-001, 3.5645197022175457e-001, 3.5700209226498419e-001,
|
||||
3.5755200461808367e-001, 3.5810170744084729e-001, 3.5865120089288688e-001, 3.5920048513363184e-001,
|
||||
3.5974956032232963e-001, 3.6029842661804584e-001, 3.6084708417966471e-001, 3.6139553316588924e-001,
|
||||
3.6194377373524150e-001, 3.6249180604606285e-001, 3.6303963025651437e-001, 3.6358724652457697e-001,
|
||||
3.6413465500805176e-001, 3.6468185586456020e-001, 3.6522884925154470e-001, 3.6577563532626839e-001,
|
||||
3.6632221424581579e-001, 3.6686858616709295e-001, 3.6741475124682760e-001, 3.6796070964156979e-001,
|
||||
3.6850646150769167e-001, 3.6905200700138807e-001, 3.6959734627867674e-001, 3.7014247949539852e-001,
|
||||
3.7068740680721768e-001, 3.7123212836962216e-001, 3.7177664433792384e-001, 3.7232095486725891e-001,
|
||||
3.7286506011258774e-001, 3.7340896022869580e-001, 3.7395265537019334e-001, 3.7449614569151596e-001,
|
||||
3.7503943134692475e-001, 3.7558251249050661e-001, 3.7612538927617450e-001, 3.7666806185766766e-001,
|
||||
3.7721053038855196e-001, 3.7775279502222003e-001, 3.7829485591189177e-001, 3.7883671321061424e-001,
|
||||
3.7937836707126216e-001, 3.7991981764653820e-001, 3.8046106508897315e-001, 3.8100210955092612e-001,
|
||||
3.8154295118458498e-001, 3.8208359014196641e-001, 3.8262402657491629e-001, 3.8316426063510989e-001,
|
||||
3.8370429247405224e-001, 3.8424412224307819e-001, 3.8478375009335281e-001, 3.8532317617587164e-001,
|
||||
3.8586240064146093e-001, 3.8640142364077773e-001, 3.8694024532431054e-001, 3.8747886584237901e-001,
|
||||
3.8801728534513480e-001, 3.8855550398256117e-001, 3.8909352190447394e-001, 3.8963133926052113e-001,
|
||||
3.9016895620018355e-001, 3.9070637287277493e-001, 3.9124358942744231e-001, 3.9178060601316611e-001,
|
||||
3.9231742277876025e-001, 3.9285403987287298e-001, 3.9339045744398649e-001, 3.9392667564041739e-001,
|
||||
3.9446269461031708e-001, 3.9499851450167184e-001, 3.9553413546230315e-001, 3.9606955763986784e-001,
|
||||
3.9660478118185849e-001, 3.9713980623560352e-001, 3.9767463294826755e-001, 3.9820926146685159e-001,
|
||||
3.9874369193819326e-001, 3.9927792450896704e-001, 3.9981195932568453e-001, 4.0034579653469476e-001,
|
||||
4.0087943628218431e-001, 4.0141287871417763e-001, 4.0194612397653712e-001, 4.0247917221496371e-001,
|
||||
4.0301202357499677e-001, 4.0354467820201440e-001, 4.0407713624123393e-001, 4.0460939783771172e-001,
|
||||
4.0514146313634392e-001, 4.0567333228186608e-001, 4.0620500541885413e-001, 4.0673648269172391e-001,
|
||||
4.0726776424473182e-001, 4.0779885022197493e-001, 4.0832974076739126e-001, 4.0886043602476002e-001,
|
||||
4.0939093613770178e-001, 4.0992124124967860e-001, 4.1045135150399464e-001, 4.1098126704379601e-001,
|
||||
4.1151098801207114e-001, 4.1204051455165103e-001, 4.1256984680520947e-001, 4.1309898491526326e-001,
|
||||
4.1362792902417245e-001, 4.1415667927414057e-001, 4.1468523580721478e-001, 4.1521359876528630e-001,
|
||||
4.1574176829009046e-001, 4.1626974452320686e-001, 4.1679752760605998e-001, 4.1732511767991892e-001,
|
||||
4.1785251488589781e-001, 4.1837971936495644e-001, 4.1890673125789957e-001, 4.1943355070537813e-001,
|
||||
4.1996017784788903e-001, 4.2048661282577515e-001, 4.2101285577922587e-001, 4.2153890684827733e-001,
|
||||
4.2206476617281236e-001, 4.2259043389256096e-001, 4.2311591014710065e-001, 4.2364119507585613e-001,
|
||||
4.2416628881810009e-001, 4.2469119151295320e-001, 4.2521590329938430e-001, 4.2574042431621051e-001,
|
||||
4.2626475470209790e-001, 4.2678889459556119e-001, 4.2731284413496412e-001, 4.2783660345851998e-001,
|
||||
4.2836017270429139e-001, 4.2888355201019068e-001, 4.2940674151398039e-001, 4.2992974135327294e-001,
|
||||
4.3045255166553137e-001, 4.3097517258806928e-001, 4.3149760425805095e-001, 4.3201984681249178e-001,
|
||||
4.3254190038825852e-001, 4.3306376512206940e-001, 4.3358544115049413e-001, 4.3410692860995465e-001,
|
||||
4.3462822763672460e-001, 4.3514933836693043e-001, 4.3567026093655065e-001, 4.3619099548141693e-001,
|
||||
4.3671154213721369e-001, 4.3723190103947868e-001, 4.3775207232360275e-001, 4.3827205612483067e-001,
|
||||
4.3879185257826087e-001, 4.3931146181884595e-001, 4.3983088398139258e-001, 4.4035011920056194e-001,
|
||||
4.4086916761086975e-001, 4.4138802934668692e-001, 4.4190670454223913e-001, 4.4242519333160735e-001,
|
||||
4.4294349584872833e-001, 4.4346161222739416e-001, 4.4397954260125300e-001, 4.4449728710380915e-001,
|
||||
4.4501484586842310e-001, 4.4553221902831214e-001, 4.4604940671654997e-001, 4.4656640906606732e-001,
|
||||
4.4708322620965224e-001, 4.4759985827994986e-001, 4.4811630540946312e-001, 4.4863256773055249e-001,
|
||||
4.4914864537543642e-001, 4.4966453847619164e-001, 4.5018024716475330e-001, 4.5069577157291496e-001,
|
||||
4.5121111183232882e-001, 4.5172626807450633e-001, 4.5224124043081809e-001, 4.5275602903249390e-001,
|
||||
4.5327063401062317e-001, 4.5378505549615517e-001, 4.5429929361989918e-001, 4.5481334851252442e-001,
|
||||
4.5532722030456074e-001, 4.5584090912639835e-001, 4.5635441510828845e-001, 4.5686773838034306e-001,
|
||||
4.5738087907253527e-001, 4.5789383731469985e-001, 4.5840661323653270e-001, 4.5891920696759192e-001,
|
||||
4.5943161863729726e-001, 4.5994384837493074e-001, 4.6045589630963657e-001, 4.6096776257042177e-001,
|
||||
4.6147944728615592e-001, 4.6199095058557149e-001, 4.6250227259726412e-001, 4.6301341344969293e-001,
|
||||
4.6352437327118023e-001, 4.6403515218991237e-001, 4.6454575033393936e-001, 4.6505616783117537e-001,
|
||||
4.6556640480939882e-001, 4.6607646139625275e-001, 4.6658633771924468e-001, 4.6709603390574705e-001,
|
||||
4.6760555008299742e-001, 4.6811488637809856e-001, 4.6862404291801857e-001, 4.6913301982959116e-001,
|
||||
4.6964181723951609e-001, 4.7015043527435896e-001, 4.7065887406055135e-001, 4.7116713372439167e-001,
|
||||
4.7167521439204446e-001, 4.7218311618954112e-001, 4.7269083924278038e-001, 4.7319838367752748e-001,
|
||||
4.7370574961941531e-001, 4.7421293719394425e-001, 4.7471994652648242e-001, 4.7522677774226557e-001,
|
||||
4.7573343096639775e-001, 4.7623990632385116e-001, 4.7674620393946637e-001, 4.7725232393795269e-001,
|
||||
4.7775826644388808e-001, 4.7826403158171960e-001, 4.7876961947576346e-001, 4.7927503025020518e-001,
|
||||
4.7978026402909968e-001, 4.8028532093637188e-001, 4.8079020109581644e-001, 4.8129490463109803e-001,
|
||||
4.8179943166575173e-001, 4.8230378232318288e-001, 4.8280795672666771e-001, 4.8331195499935287e-001,
|
||||
4.8381577726425645e-001, 4.8431942364426717e-001, 4.8482289426214564e-001, 4.8532618924052362e-001,
|
||||
4.8582930870190477e-001, 4.8633225276866437e-001, 4.8683502156305014e-001, 4.8733761520718172e-001,
|
||||
4.8784003382305130e-001, 4.8834227753252368e-001, 4.8884434645733643e-001, 4.8934624071909993e-001,
|
||||
4.8984796043929779e-001, 4.9034950573928698e-001, 4.9085087674029787e-001, 4.9135207356343452e-001,
|
||||
4.9185309632967472e-001, 4.9235394515987035e-001, 4.9285462017474752e-001, 4.9335512149490657e-001,
|
||||
4.9385544924082253e-001, 4.9435560353284480e-001, 4.9485558449119810e-001, 4.9535539223598191e-001,
|
||||
4.9585502688717098e-001, 4.9635448856461545e-001, 4.9685377738804115e-001, 4.9735289347704947e-001,
|
||||
4.9785183695111779e-001, 4.9835060792959962e-001, 4.9884920653172460e-001, 4.9934763287659895e-001,
|
||||
4.9984588708320532e-001, 5.0034396927040325e-001, 5.0084187955692927e-001, 5.0133961806139682e-001,
|
||||
5.0183718490229678e-001, 5.0233458019799737e-001, 5.0283180406674444e-001, 5.0332885662666160e-001,
|
||||
5.0382573799575070e-001, 5.0432244829189121e-001, 5.0481898763284128e-001, 5.0531535613623724e-001,
|
||||
5.0581155391959443e-001, 5.0630758110030638e-001, 5.0680343779564618e-001, 5.0729912412276579e-001,
|
||||
5.0779464019869625e-001, 5.0828998614034848e-001, 5.0878516206451252e-001, 5.0928016808785881e-001,
|
||||
5.0977500432693701e-001, 5.1026967089817765e-001, 5.1076416791789092e-001, 5.1125849550226776e-001,
|
||||
5.1175265376737955e-001, 5.1224664282917876e-001, 5.1274046280349828e-001, 5.1323411380605266e-001,
|
||||
5.1372759595243733e-001, 5.1422090935812914e-001, 5.1471405413848692e-001, 5.1520703040875060e-001,
|
||||
5.1569983828404242e-001, 5.1619247787936684e-001, 5.1668494930961018e-001, 5.1717725268954140e-001,
|
||||
5.1766938813381191e-001, 5.1816135575695565e-001, 5.1865315567338988e-001, 5.1914478799741448e-001,
|
||||
5.1963625284321280e-001, 5.2012755032485114e-001, 5.2061868055627991e-001, 5.2110964365133250e-001,
|
||||
5.2160043972372672e-001, 5.2209106888706402e-001, 5.2258153125483009e-001, 5.2307182694039489e-001,
|
||||
5.2356195605701283e-001, 5.2405191871782297e-001, 5.2454171503584923e-001, 5.2503134512400018e-001,
|
||||
5.2552080909506971e-001, 5.2601010706173679e-001, 5.2649923913656593e-001, 5.2698820543200708e-001,
|
||||
5.2747700606039605e-001, 5.2796564113395417e-001, 5.2845411076478921e-001, 5.2894241506489481e-001,
|
||||
5.2943055414615103e-001, 5.2991852812032414e-001, 5.3040633709906770e-001, 5.3089398119392139e-001,
|
||||
5.3138146051631208e-001, 5.3186877517755382e-001, 5.3235592528884768e-001, 5.3284291096128233e-001,
|
||||
5.3332973230583380e-001, 5.3381638943336596e-001, 5.3430288245463053e-001, 5.3478921148026692e-001,
|
||||
5.3527537662080327e-001, 5.3576137798665546e-001, 5.3624721568812816e-001, 5.3673288983541445e-001,
|
||||
5.3721840053859626e-001, 5.3770374790764430e-001, 5.3818893205241869e-001, 5.3867395308266830e-001,
|
||||
5.3915881110803132e-001, 5.3964350623803614e-001, 5.4012803858209990e-001, 5.4061240824953005e-001,
|
||||
5.4109661534952391e-001, 5.4158065999116878e-001, 5.4206454228344236e-001, 5.4254826233521247e-001,
|
||||
5.4303182025523777e-001, 5.4351521615216736e-001, 5.4399845013454129e-001, 5.4448152231079050e-001,
|
||||
5.4496443278923712e-001, 5.4544718167809458e-001, 5.4592976908546731e-001, 5.4641219511935213e-001,
|
||||
5.4689445988763674e-001, 5.4737656349810093e-001, 5.4785850605841668e-001, 5.4834028767614795e-001,
|
||||
5.4882190845875112e-001, 5.4930336851357453e-001, 5.4978466794785952e-001, 5.5026580686874027e-001,
|
||||
5.5074678538324318e-001, 5.5122760359828826e-001, 5.5170826162068842e-001, 5.5218875955714974e-001,
|
||||
5.5266909751427185e-001, 5.5314927559854776e-001, 5.5362929391636462e-001, 5.5410915257400295e-001,
|
||||
5.5458885167763738e-001, 5.5506839133333685e-001, 5.5554777164706448e-001, 5.5602699272467748e-001,
|
||||
5.5650605467192815e-001, 5.5698495759446309e-001, 5.5746370159782399e-001, 5.5794228678744717e-001,
|
||||
5.5842071326866427e-001, 5.5889898114670222e-001, 5.5937709052668327e-001, 5.5985504151362508e-001,
|
||||
5.6033283421244118e-001, 5.6081046872794083e-001, 5.6128794516482916e-001, 5.6176526362770718e-001,
|
||||
5.6224242422107273e-001, 5.6271942704931921e-001, 5.6319627221673718e-001, 5.6367295982751342e-001,
|
||||
5.6414948998573167e-001, 5.6462586279537241e-001, 5.6510207836031323e-001, 5.6557813678432900e-001,
|
||||
5.6605403817109168e-001, 5.6652978262417075e-001, 5.6700537024703346e-001, 5.6748080114304444e-001,
|
||||
5.6795607541546633e-001, 5.6843119316745983e-001, 5.6890615450208348e-001, 5.6938095952229439e-001,
|
||||
5.6985560833094784e-001, 5.7033010103079773e-001, 5.7080443772449674e-001, 5.7127861851459583e-001,
|
||||
5.7175264350354538e-001, 5.7222651279369474e-001, 5.7270022648729224e-001, 5.7317378468648561e-001,
|
||||
5.7364718749332200e-001, 5.7412043500974841e-001, 5.7459352733761093e-001, 5.7506646457865618e-001,
|
||||
5.7553924683453039e-001, 5.7601187420678002e-001, 5.7648434679685145e-001, 5.7695666470609186e-001,
|
||||
5.7742882803574869e-001, 5.7790083688696992e-001, 5.7837269136080471e-001, 5.7884439155820244e-001,
|
||||
5.7931593758001410e-001, 5.7978732952699152e-001, 5.8025856749978777e-001, 5.8072965159895773e-001,
|
||||
5.8120058192495705e-001, 5.8167135857814389e-001, 5.8214198165877751e-001, 5.8261245126701955e-001,
|
||||
5.8308276750293342e-001, 5.8355293046648482e-001, 5.8402294025754176e-001, 5.8449279697587453e-001,
|
||||
5.8496250072115619e-001, 5.8543205159296230e-001, 5.8590144969077129e-001, 5.8637069511396456e-001,
|
||||
5.8683978796182656e-001, 5.8730872833354475e-001, 5.8777751632821018e-001, 5.8824615204481723e-001,
|
||||
5.8871463558226367e-001, 5.8918296703935125e-001, 5.8965114651478534e-001, 5.9011917410717518e-001,
|
||||
5.9058704991503430e-001, 5.9105477403678031e-001, 5.9152234657073499e-001, 5.9198976761512467e-001,
|
||||
5.9245703726808041e-001, 5.9292415562763767e-001, 5.9339112279173689e-001, 5.9385793885822336e-001,
|
||||
5.9432460392484743e-001, 5.9479111808926455e-001, 5.9525748144903567e-001, 5.9572369410162695e-001,
|
||||
5.9618975614441028e-001, 5.9665566767466305e-001, 5.9712142878956842e-001, 5.9758703958621573e-001,
|
||||
5.9805250016159994e-001, 5.9851781061262244e-001, 5.9898297103609077e-001, 5.9944798152871903e-001,
|
||||
5.9991284218712770e-001, 6.0037755310784380e-001, 6.0084211438730128e-001, 6.0130652612184077e-001,
|
||||
6.0177078840771003e-001, 6.0223490134106406e-001, 6.0269886501796488e-001, 6.0316267953438185e-001,
|
||||
6.0362634498619194e-001, 6.0408986146917965e-001, 6.0455322907903697e-001, 6.0501644791136422e-001,
|
||||
6.0547951806166922e-001, 6.0594243962536798e-001, 6.0640521269778469e-001, 6.0686783737415195e-001,
|
||||
6.0733031374961077e-001, 6.0779264191921034e-001, 6.0825482197790881e-001, 6.0871685402057329e-001,
|
||||
6.0917873814197931e-001, 6.0964047443681180e-001, 6.1010206299966441e-001, 6.1056350392504044e-001,
|
||||
6.1102479730735226e-001, 6.1148594324092187e-001, 6.1194694181998066e-001, 6.1240779313866989e-001,
|
||||
6.1286849729104065e-001, 6.1332905437105378e-001, 6.1378946447258032e-001, 6.1424972768940123e-001,
|
||||
6.1470984411520824e-001, 6.1516981384360292e-001, 6.1562963696809758e-001, 6.1608931358211516e-001,
|
||||
6.1654884377898933e-001, 6.1700822765196461e-001, 6.1746746529419627e-001, 6.1792655679875097e-001,
|
||||
6.1838550225860645e-001, 6.1884430176665151e-001, 6.1930295541568670e-001, 6.1976146329842374e-001,
|
||||
6.2021982550748633e-001, 6.2067804213540978e-001, 6.2113611327464113e-001, 6.2159403901753962e-001,
|
||||
6.2205181945637633e-001, 6.2250945468333463e-001, 6.2296694479051018e-001, 6.2342428986991139e-001,
|
||||
6.2388149001345850e-001, 6.2433854531298505e-001, 6.2479545586023699e-001, 6.2525222174687300e-001,
|
||||
6.2570884306446528e-001, 6.2616531990449842e-001, 6.2662165235837075e-001, 6.2707784051739368e-001,
|
||||
6.2753388447279190e-001, 6.2798978431570363e-001, 6.2844554013718112e-001, 6.2890115202818997e-001,
|
||||
6.2935662007960957e-001, 6.2981194438223365e-001, 6.3026712502676951e-001, 6.3072216210383913e-001,
|
||||
6.3117705570397842e-001, 6.3163180591763768e-001, 6.3208641283518197e-001, 6.3254087654689062e-001,
|
||||
6.3299519714295782e-001, 6.3344937471349272e-001, 6.3390340934851908e-001, 6.3435730113797606e-001,
|
||||
6.3481105017171768e-001, 6.3526465653951336e-001, 6.3571812033104769e-001, 6.3617144163592099e-001,
|
||||
6.3662462054364888e-001, 6.3707765714366271e-001, 6.3753055152530980e-001, 6.3798330377785317e-001,
|
||||
6.3843591399047173e-001, 6.3888838225226086e-001, 6.3934070865223169e-001, 6.3979289327931199e-001,
|
||||
6.4024493622234580e-001, 6.4069683757009366e-001, 6.4114859741123253e-001, 6.4160021583435678e-001,
|
||||
6.4205169292797670e-001, 6.4250302878052035e-001, 6.4295422348033204e-001, 6.4340527711567386e-001,
|
||||
6.4385618977472470e-001, 6.4430696154558109e-001, 6.4475759251625697e-001, 6.4520808277468356e-001,
|
||||
6.4565843240871013e-001, 6.4610864150610336e-001, 6.4655871015454802e-001, 6.4700863844164680e-001,
|
||||
6.4745842645492035e-001, 6.4790807428180752e-001, 6.4835758200966542e-001, 6.4880694972576980e-001,
|
||||
6.4925617751731446e-001, 6.4970526547141205e-001, 6.5015421367509374e-001, 6.5060302221530975e-001,
|
||||
6.5105169117892858e-001, 6.5150022065273849e-001, 6.5194861072344634e-001, 6.5239686147767806e-001,
|
||||
6.5284497300197930e-001, 6.5329294538281479e-001, 6.5374077870656877e-001, 6.5418847305954508e-001,
|
||||
6.5463602852796732e-001, 6.5508344519797879e-001, 6.5553072315564265e-001, 6.5597786248694212e-001,
|
||||
6.5642486327778027e-001, 6.5687172561398077e-001, 6.5731844958128727e-001, 6.5776503526536367e-001,
|
||||
6.5821148275179464e-001, 6.5865779212608544e-001, 6.5910396347366162e-001, 6.5954999687986982e-001,
|
||||
6.5999589242997758e-001, 6.6044165020917311e-001, 6.6088727030256589e-001, 6.6133275279518655e-001,
|
||||
6.6177809777198704e-001, 6.6222330531784046e-001, 6.6266837551754154e-001, 6.6311330845580641e-001,
|
||||
6.6355810421727301e-001, 6.6400276288650084e-001, 6.6444728454797142e-001, 6.6489166928608812e-001,
|
||||
6.6533591718517626e-001, 6.6578002832948335e-001, 6.6622400280317917e-001, 6.6666784069035590e-001,
|
||||
6.6711154207502776e-001, 6.6755510704113186e-001, 6.6799853567252787e-001, 6.6844182805299779e-001,
|
||||
6.6888498426624710e-001, 6.6932800439590334e-001, 6.6977088852551769e-001, 6.7021363673856416e-001,
|
||||
6.7065624911843991e-001, 6.7109872574846541e-001, 6.7154106671188440e-001, 6.7198327209186415e-001,
|
||||
6.7242534197149562e-001, 6.7286727643379307e-001, 6.7330907556169495e-001, 6.7375073943806307e-001,
|
||||
6.7419226814568345e-001, 6.7463366176726602e-001, 6.7507492038544492e-001, 6.7551604408277832e-001,
|
||||
6.7595703294174880e-001, 6.7639788704476345e-001, 6.7683860647415350e-001, 6.7727919131217496e-001,
|
||||
6.7771964164100862e-001, 6.7815995754275982e-001, 6.7860013909945882e-001, 6.7904018639306085e-001,
|
||||
6.7948009950544597e-001, 6.7991987851841984e-001, 6.8035952351371276e-001, 6.8079903457298063e-001,
|
||||
6.8123841177780486e-001, 6.8167765520969192e-001, 6.8211676495007434e-001, 6.8255574108031014e-001,
|
||||
6.8299458368168287e-001, 6.8343329283540233e-001, 6.8387186862260385e-001, 6.8431031112434904e-001,
|
||||
6.8474862042162565e-001, 6.8518679659534754e-001, 6.8562483972635480e-001, 6.8606274989541416e-001,
|
||||
6.8650052718321841e-001, 6.8693817167038729e-001, 6.8737568343746713e-001, 6.8781306256493080e-001,
|
||||
6.8825030913317820e-001, 6.8868742322253595e-001, 6.8912440491325799e-001, 6.8956125428552517e-001,
|
||||
6.8999797141944541e-001, 6.9043455639505402e-001, 6.9087100929231382e-001, 6.9130733019111479e-001,
|
||||
6.9174351917127475e-001, 6.9217957631253879e-001, 6.9261550169458008e-001, 6.9305129539699928e-001,
|
||||
6.9348695749932521e-001, 6.9392248808101453e-001, 6.9435788722145175e-001, 6.9479315499995009e-001,
|
||||
6.9522829149575038e-001, 6.9566329678802219e-001, 6.9609817095586313e-001, 6.9653291407829987e-001,
|
||||
6.9696752623428715e-001, 6.9740200750270842e-001, 6.9783635796237620e-001, 6.9827057769203149e-001,
|
||||
6.9870466677034448e-001, 6.9913862527591430e-001, 6.9957245328726902e-001, 7.0000615088286611e-001,
|
||||
7.0043971814109218e-001, 7.0087315514026305e-001, 7.0130646195862456e-001, 7.0173963867435107e-001,
|
||||
7.0217268536554767e-001, 7.0260560211024814e-001, 7.0303838898641680e-001, 7.0347104607194733e-001,
|
||||
7.0390357344466359e-001, 7.0433597118231950e-001, 7.0476823936259880e-001, 7.0520037806311564e-001,
|
||||
7.0563238736141454e-001, 7.0606426733497030e-001, 7.0649601806118789e-001, 7.0692763961740335e-001,
|
||||
7.0735913208088275e-001, 7.0779049552882334e-001, 7.0822173003835265e-001, 7.0865283568652959e-001,
|
||||
7.0908381255034381e-001, 7.0951466070671576e-001, 7.0994538023249720e-001, 7.1037597120447105e-001,
|
||||
7.1080643369935159e-001, 7.1123676779378442e-001, 7.1166697356434627e-001, 7.1209705108754584e-001,
|
||||
7.1252700043982320e-001, 7.1295682169755015e-001, 7.1338651493703009e-001, 7.1381608023449850e-001,
|
||||
7.1424551766612254e-001, 7.1467482730800180e-001, 7.1510400923616735e-001, 7.1553306352658286e-001,
|
||||
7.1596199025514418e-001, 7.1639078949767943e-001, 7.1681946132994900e-001, 7.1724800582764603e-001,
|
||||
7.1767642306639601e-001, 7.1810471312175739e-001, 7.1853287606922078e-001, 7.1896091198421030e-001,
|
||||
7.1938882094208223e-001, 7.1981660301812656e-001, 7.2024425828756578e-001, 7.2067178682555566e-001,
|
||||
7.2109918870718503e-001, 7.2152646400747666e-001, 7.2195361280138581e-001, 7.2238063516380147e-001,
|
||||
7.2280753116954666e-001, 7.2323430089337726e-001, 7.2366094440998319e-001, 7.2408746179398831e-001,
|
||||
7.2451385311994976e-001, 7.2494011846235917e-001, 7.2536625789564169e-001, 7.2579227149415704e-001,
|
||||
7.2621815933219858e-001, 7.2664392148399426e-001, 7.2706955802370621e-001, 7.2749506902543093e-001,
|
||||
7.2792045456319909e-001, 7.2834571471097664e-001, 7.2877084954266336e-001, 7.2919585913209406e-001,
|
||||
7.2962074355303852e-001, 7.3004550287920089e-001, 7.3047013718422060e-001, 7.3089464654167180e-001,
|
||||
7.3131903102506413e-001, 7.3174329070784194e-001, 7.3216742566338500e-001, 7.3259143596500842e-001,
|
||||
7.3301532168596262e-001, 7.3343908289943360e-001, 7.3386271967854244e-001, 7.3428623209634647e-001,
|
||||
7.3470962022583819e-001, 7.3513288413994626e-001, 7.3555602391153485e-001, 7.3597903961340405e-001,
|
||||
7.3640193131829024e-001, 7.3682469909886539e-001, 7.3724734302773809e-001, 7.3766986317745276e-001,
|
||||
7.3809225962049041e-001, 7.3851453242926801e-001, 7.3893668167613935e-001, 7.3935870743339449e-001,
|
||||
7.3978060977326021e-001, 7.4020238876789979e-001, 7.4062404448941344e-001, 7.4104557700983786e-001,
|
||||
7.4146698640114694e-001, 7.4188827273525138e-001, 7.4230943608399902e-001, 7.4273047651917445e-001,
|
||||
7.4315139411249986e-001, 7.4357218893563448e-001, 7.4399286106017470e-001, 7.4441341055765453e-001,
|
||||
7.4483383749954557e-001, 7.4525414195725659e-001, 7.4567432400213407e-001, 7.4609438370546244e-001,
|
||||
7.4651432113846361e-001, 7.4693413637229711e-001, 7.4735382947806106e-001, 7.4777340052679087e-001,
|
||||
7.4819284958946031e-001, 7.4861217673698111e-001, 7.4903138204020348e-001, 7.4945046556991557e-001,
|
||||
7.4986942739684392e-001, 7.5028826759165368e-001, 7.5070698622494814e-001, 7.5112558336726942e-001,
|
||||
7.5154405908909816e-001, 7.5196241346085357e-001, 7.5238064655289383e-001, 7.5279875843551569e-001,
|
||||
7.5321674917895498e-001, 7.5363461885338667e-001, 7.5405236752892424e-001, 7.5446999527562086e-001,
|
||||
7.5488750216346856e-001, 7.5530488826239861e-001, 7.5572215364228179e-001, 7.5613929837292804e-001,
|
||||
7.5655632252408700e-001, 7.5697322616544760e-001, 7.5739000936663869e-001, 7.5780667219722841e-001,
|
||||
7.5822321472672494e-001, 7.5863963702457604e-001, 7.5905593916016967e-001, 7.5947212120283336e-001,
|
||||
7.5988818322183493e-001, 7.6030412528638236e-001, 7.6071994746562333e-001, 7.6113564982864601e-001,
|
||||
7.6155123244447931e-001, 7.6196669538209183e-001, 7.6238203871039278e-001, 7.6279726249823210e-001,
|
||||
7.6321236681440019e-001, 7.6362735172762797e-001, 7.6404221730658717e-001, 7.6445696361989024e-001,
|
||||
7.6487159073609068e-001, 7.6528609872368247e-001, 7.6570048765110110e-001, 7.6611475758672265e-001,
|
||||
7.6652890859886458e-001, 7.6694294075578540e-001, 7.6735685412568511e-001, 7.6777064877670453e-001,
|
||||
7.6818432477692644e-001, 7.6859788219437453e-001, 7.6901132109701453e-001, 7.6942464155275347e-001,
|
||||
7.6983784362943997e-001, 7.7025092739486467e-001, 7.7066389291675952e-001, 7.7107674026279871e-001,
|
||||
7.7148946950059838e-001, 7.7190208069771626e-001, 7.7231457392165259e-001, 7.7272694923984941e-001,
|
||||
7.7313920671969116e-001, 7.7355134642850432e-001, 7.7396336843355784e-001, 7.7437527280206298e-001,
|
||||
7.7478705960117344e-001, 7.7519872889798547e-001, 7.7561028075953808e-001, 7.7602171525281249e-001,
|
||||
7.7643303244473305e-001, 7.7684423240216660e-001, 7.7725531519192304e-001, 7.7766628088075496e-001,
|
||||
7.7807712953535813e-001, 7.7848786122237124e-001, 7.7889847600837614e-001, 7.7930897395989762e-001,
|
||||
7.7971935514340407e-001, 7.8012961962530680e-001, 7.8053976747196097e-001, 7.8094979874966464e-001,
|
||||
7.8135971352465960e-001, 7.8176951186313115e-001, 7.8217919383120837e-001, 7.8258875949496365e-001,
|
||||
7.8299820892041361e-001, 7.8340754217351827e-001, 7.8381675932018158e-001, 7.8422586042625175e-001,
|
||||
7.8463484555752050e-001, 7.8504371477972412e-001, 7.8545246815854264e-001, 7.8586110575960044e-001,
|
||||
7.8626962764846597e-001, 7.8667803389065238e-001, 7.8708632455161687e-001, 7.8749449969676122e-001,
|
||||
7.8790255939143161e-001, 7.8831050370091882e-001, 7.8871833269045832e-001, 7.8912604642523043e-001,
|
||||
7.8953364497035983e-001, 7.8994112839091613e-001, 7.9034849675191421e-001, 7.9075575011831345e-001,
|
||||
7.9116288855501826e-001, 7.9156991212687855e-001, 7.9197682089868859e-001, 7.9238361493518872e-001,
|
||||
7.9279029430106385e-001, 7.9319685906094461e-001, 7.9360330927940681e-001, 7.9400964502097160e-001,
|
||||
7.9441586635010597e-001, 7.9482197333122218e-001, 7.9522796602867818e-001, 7.9563384450677765e-001,
|
||||
7.9603960882976998e-001, 7.9644525906185037e-001, 7.9685079526715985e-001, 7.9725621750978548e-001,
|
||||
7.9766152585376004e-001, 7.9806672036306292e-001, 7.9847180110161886e-001, 7.9887676813329944e-001,
|
||||
7.9928162152192206e-001, 7.9968636133125037e-001, 8.0009098762499486e-001, 8.0049550046681195e-001,
|
||||
8.0089989992030475e-001, 8.0130418604902276e-001, 8.0170835891646197e-001, 8.0211241858606563e-001,
|
||||
8.0251636512122282e-001, 8.0292019858527008e-001, 8.0332391904149059e-001, 8.0372752655311419e-001,
|
||||
8.0413102118331770e-001, 8.0453440299522549e-001, 8.0493767205190825e-001, 8.0534082841638421e-001,
|
||||
8.0574387215161880e-001, 8.0614680332052446e-001, 8.0654962198596114e-001, 8.0695232821073626e-001,
|
||||
8.0735492205760406e-001, 8.0775740358926706e-001, 8.0815977286837482e-001, 8.0856202995752446e-001,
|
||||
8.0896417491926120e-001, 8.0936620781607749e-001, 8.0976812871041370e-001, 8.1016993766465817e-001,
|
||||
8.1057163474114702e-001, 8.1097322000216410e-001, 8.1137469350994185e-001, 8.1177605532666008e-001,
|
||||
8.1217730551444733e-001, 8.1257844413537983e-001, 8.1297947125148218e-001, 8.1338038692472758e-001,
|
||||
8.1378119121703707e-001, 8.1418188419028048e-001, 8.1458246590627603e-001, 8.1498293642679021e-001,
|
||||
8.1538329581353852e-001, 8.1578354412818488e-001, 8.1618368143234155e-001, 8.1658370778757028e-001,
|
||||
8.1698362325538099e-001, 8.1738342789723262e-001, 8.1778312177453327e-001, 8.1818270494863987e-001,
|
||||
8.1858217748085815e-001, 8.1898153943244345e-001, 8.1938079086459958e-001, 8.1977993183848019e-001,
|
||||
8.2017896241518773e-001, 8.2057788265577425e-001, 8.2097669262124096e-001, 8.2137539237253876e-001,
|
||||
8.2177398197056772e-001, 8.2217246147617762e-001, 8.2257083095016792e-001, 8.2296909045328748e-001,
|
||||
8.2336724004623507e-001, 8.2376527978965919e-001, 8.2416320974415802e-001, 8.2456102997027969e-001,
|
||||
8.2495874052852247e-001, 8.2535634147933423e-001, 8.2575383288311299e-001, 8.2615121480020715e-001,
|
||||
8.2654848729091501e-001, 8.2694565041548485e-001, 8.2734270423411582e-001, 8.2773964880695672e-001,
|
||||
8.2813648419410713e-001, 8.2853321045561679e-001, 8.2892982765148637e-001, 8.2932633584166637e-001,
|
||||
8.2972273508605854e-001, 8.3011902544451499e-001, 8.3051520697683834e-001, 8.3091127974278201e-001,
|
||||
8.3130724380205079e-001, 8.3170309921429941e-001, 8.3209884603913431e-001, 8.3249448433611228e-001,
|
||||
8.3289001416474162e-001, 8.3328543558448143e-001, 8.3368074865474195e-001, 8.3407595343488461e-001,
|
||||
8.3447104998422206e-001, 8.3486603836201823e-001, 8.3526091862748864e-001, 8.3565569083979963e-001,
|
||||
8.3605035505806968e-001, 8.3644491134136822e-001, 8.3683935974871659e-001, 8.3723370033908728e-001,
|
||||
8.3762793317140483e-001, 8.3802205830454546e-001, 8.3841607579733712e-001, 8.3880998570855936e-001,
|
||||
8.3920378809694385e-001, 8.3959748302117421e-001, 8.3999107053988564e-001, 8.4038455071166585e-001,
|
||||
8.4077792359505443e-001, 8.4117118924854284e-001, 8.4156434773057509e-001, 8.4195739909954737e-001,
|
||||
8.4235034341380788e-001, 8.4274318073165755e-001, 8.4313591111134945e-001, 8.4352853461108901e-001,
|
||||
8.4392105128903439e-001, 8.4431346120329631e-001, 8.4470576441193768e-001, 8.4509796097297452e-001,
|
||||
8.4549005094437524e-001, 8.4588203438406107e-001, 8.4627391134990648e-001, 8.4666568189973801e-001,
|
||||
8.4705734609133565e-001, 8.4744890398243211e-001, 8.4784035563071314e-001, 8.4823170109381774e-001,
|
||||
8.4862294042933795e-001, 8.4901407369481863e-001, 8.4940510094775834e-001, 8.4979602224560857e-001,
|
||||
8.5018683764577418e-001, 8.5057754720561352e-001, 8.5096815098243817e-001, 8.5135864903351322e-001,
|
||||
8.5174904141605756e-001, 8.5213932818724314e-001, 8.5252950940419581e-001, 8.5291958512399524e-001,
|
||||
8.5330955540367448e-001, 8.5369942030022050e-001, 8.5408917987057409e-001, 8.5447883417162984e-001,
|
||||
8.5486838326023640e-001, 8.5525782719319610e-001, 8.5564716602726576e-001, 8.5603639981915569e-001,
|
||||
8.5642552862553079e-001, 8.5681455250300964e-001, 8.5720347150816567e-001, 8.5759228569752588e-001,
|
||||
8.5798099512757209e-001, 8.5836959985474026e-001, 8.5875809993542074e-001, 8.5914649542595867e-001,
|
||||
8.5953478638265290e-001, 8.5992297286175767e-001, 8.6031105491948145e-001, 8.6069903261198766e-001,
|
||||
8.6108690599539373e-001, 8.6147467512577258e-001, 8.6186234005915152e-001, 8.6224990085151287e-001,
|
||||
8.6263735755879367e-001, 8.6302471023688632e-001, 8.6341195894163758e-001, 8.6379910372884983e-001,
|
||||
8.6418614465428023e-001, 8.6457308177364123e-001, 8.6495991514260018e-001, 8.6534664481677981e-001,
|
||||
8.6573327085175877e-001, 8.6611979330306987e-001, 8.6650621222620194e-001, 8.6689252767659952e-001,
|
||||
8.6727873970966185e-001, 8.6766484838074454e-001, 8.6805085374515811e-001, 8.6843675585816893e-001,
|
||||
8.6882255477499892e-001, 8.6920825055082629e-001, 8.6959384324078404e-001, 8.6997933289996177e-001,
|
||||
8.7036471958340444e-001, 8.7075000334611330e-001, 8.7113518424304537e-001, 8.7152026232911350e-001,
|
||||
8.7190523765918682e-001, 8.7229011028809045e-001, 8.7267488027060558e-001, 8.7305954766146976e-001,
|
||||
8.7344411251537646e-001, 8.7382857488697596e-001, 8.7421293483087414e-001, 8.7459719240163414e-001,
|
||||
8.7498134765377444e-001, 8.7536540064177093e-001, 8.7574935142005550e-001, 8.7613320004301665e-001,
|
||||
8.7651694656499968e-001, 8.7690059104030638e-001, 8.7728413352319523e-001, 8.7766757406788121e-001,
|
||||
8.7805091272853664e-001, 8.7843414955929056e-001, 8.7881728461422814e-001, 8.7920031794739262e-001,
|
||||
8.7958324961278311e-001, 8.7996607966435669e-001, 8.8034880815602667e-001, 8.8073143514166397e-001,
|
||||
8.8111396067509662e-001, 8.8149638481010961e-001, 8.8187870760044540e-001, 8.8226092909980358e-001,
|
||||
8.8264304936184135e-001, 8.8302506844017270e-001, 8.8340698638836979e-001, 8.8378880325996145e-001,
|
||||
8.8417051910843503e-001, 8.8455213398723431e-001, 8.8493364794976137e-001, 8.8531506104937596e-001,
|
||||
8.8569637333939533e-001, 8.8607758487309396e-001, 8.8645869570370506e-001, 8.8683970588441929e-001,
|
||||
8.8722061546838504e-001, 8.8760142450870849e-001, 8.8798213305845408e-001, 8.8836274117064418e-001,
|
||||
8.8874324889825906e-001, 8.8912365629423729e-001, 8.8950396341147564e-001, 8.8988417030282874e-001,
|
||||
8.9026427702110933e-001, 8.9064428361908921e-001, 8.9102419014949741e-001, 8.9140399666502235e-001,
|
||||
8.9178370321831013e-001, 8.9216330986196557e-001, 8.9254281664855206e-001, 8.9292222363059115e-001,
|
||||
8.9330153086056352e-001, 8.9368073839090778e-001, 8.9405984627402180e-001, 8.9443885456226213e-001,
|
||||
8.9481776330794338e-001, 8.9519657256334006e-001, 8.9557528238068440e-001, 8.9595389281216808e-001,
|
||||
8.9633240390994184e-001, 8.9671081572611511e-001, 8.9708912831275611e-001, 8.9746734172189280e-001,
|
||||
8.9784545600551147e-001, 8.9822347121555823e-001, 8.9860138740393758e-001, 8.9897920462251413e-001,
|
||||
8.9935692292311098e-001, 8.9973454235751127e-001, 9.0011206297745650e-001, 9.0048948483464875e-001,
|
||||
9.0086680798074847e-001, 9.0124403246737650e-001, 9.0162115834611234e-001, 9.0199818566849566e-001,
|
||||
9.0237511448602559e-001, 9.0275194485016075e-001, 9.0312867681231956e-001, 9.0350531042387994e-001,
|
||||
9.0388184573618013e-001, 9.0425828280051801e-001, 9.0463462166815056e-001, 9.0501086239029582e-001,
|
||||
9.0538700501813074e-001, 9.0576304960279319e-001, 9.0613899619538019e-001, 9.0651484484694955e-001,
|
||||
9.0689059560851848e-001, 9.0726624853106508e-001, 9.0764180366552705e-001, 9.0801726106280278e-001,
|
||||
9.0839262077375038e-001, 9.0876788284918897e-001, 9.0914304733989726e-001, 9.0951811429661489e-001,
|
||||
9.0989308377004208e-001, 9.1026795581083852e-001, 9.1064273046962607e-001, 9.1101740779698570e-001,
|
||||
9.1139198784345932e-001, 9.1176647065955008e-001, 9.1214085629572106e-001, 9.1251514480239659e-001,
|
||||
9.1288933622996160e-001, 9.1326343062876181e-001, 9.1363742804910353e-001, 9.1401132854125444e-001,
|
||||
9.1438513215544270e-001, 9.1475883894185794e-001, 9.1513244895065038e-001, 9.1550596223193115e-001,
|
||||
9.1587937883577308e-001, 9.1625269881220983e-001, 9.1662592221123584e-001, 9.1699904908280705e-001,
|
||||
9.1737207947684118e-001, 9.1774501344321646e-001, 9.1811785103177268e-001, 9.1849059229231111e-001,
|
||||
9.1886323727459451e-001, 9.1923578602834688e-001, 9.1960823860325380e-001, 9.1998059504896212e-001,
|
||||
9.2035285541508105e-001, 9.2072501975118048e-001, 9.2109708810679236e-001, 9.2146906053141053e-001,
|
||||
9.2184093707449000e-001, 9.2221271778544811e-001, 9.2258440271366371e-001, 9.2295599190847766e-001,
|
||||
9.2332748541919218e-001, 9.2369888329507233e-001, 9.2407018558534448e-001, 9.2444139233919720e-001,
|
||||
9.2481250360578093e-001, 9.2518351943420840e-001, 9.2555443987355435e-001, 9.2592526497285566e-001,
|
||||
9.2629599478111158e-001, 9.2666662934728328e-001, 9.2703716872029474e-001, 9.2740761294903151e-001,
|
||||
9.2777796208234220e-001, 9.2814821616903731e-001, 9.2851837525789005e-001, 9.2888843939763588e-001,
|
||||
9.2925840863697295e-001, 9.2962828302456202e-001, 9.2999806260902640e-001, 9.3036774743895168e-001,
|
||||
9.3073733756288624e-001, 9.3110683302934172e-001, 9.3147623388679179e-001, 9.3184554018367316e-001,
|
||||
9.3221475196838521e-001, 9.3258386928929071e-001, 9.3295289219471467e-001, 9.3332182073294523e-001,
|
||||
9.3369065495223380e-001, 9.3405939490079426e-001, 9.3442804062680396e-001, 9.3479659217840305e-001,
|
||||
9.3516504960369495e-001, 9.3553341295074632e-001, 9.3590168226758697e-001, 9.3626985760220960e-001,
|
||||
9.3663793900257053e-001, 9.3700592651658943e-001, 9.3737382019214899e-001, 9.3774162007709538e-001,
|
||||
9.3810932621923859e-001, 9.3847693866635173e-001, 9.3884445746617096e-001, 9.3921188266639699e-001,
|
||||
9.3957921431469305e-001, 9.3994645245868669e-001, 9.4031359714596874e-001, 9.4068064842409405e-001,
|
||||
9.4104760634058060e-001, 9.4141447094291075e-001, 9.4178124227853011e-001, 9.4214792039484874e-001,
|
||||
9.4251450533923986e-001, 9.4288099715904095e-001, 9.4324739590155360e-001, 9.4361370161404301e-001,
|
||||
9.4397991434373862e-001, 9.4434603413783369e-001, 9.4471206104348582e-001, 9.4507799510781654e-001,
|
||||
9.4544383637791152e-001, 9.4580958490082090e-001, 9.4617524072355874e-001, 9.4654080389310347e-001,
|
||||
9.4690627445639775e-001, 9.4727165246034861e-001, 9.4763693795182757e-001, 9.4800213097767061e-001,
|
||||
9.4836723158467762e-001, 9.4873223981961374e-001, 9.4909715572920783e-001, 9.4946197936015420e-001,
|
||||
9.4982671075911085e-001, 9.5019134997270083e-001, 9.5055589704751198e-001, 9.5092035203009662e-001,
|
||||
9.5128471496697198e-001, 9.5164898590461966e-001, 9.5201316488948640e-001, 9.5237725196798384e-001,
|
||||
9.5274124718648812e-001, 9.5310515059134082e-001, 9.5346896222884792e-001, 9.5383268214528061e-001,
|
||||
9.5419631038687525e-001, 9.5455984699983287e-001, 9.5492329203032000e-001, 9.5528664552446818e-001,
|
||||
9.5564990752837387e-001, 9.5601307808809877e-001, 9.5637615724967007e-001, 9.5673914505908009e-001,
|
||||
9.5710204156228629e-001, 9.5746484680521138e-001, 9.5782756083374387e-001, 9.5819018369373754e-001,
|
||||
9.5855271543101106e-001, 9.5891515609134959e-001, 9.5927750572050263e-001, 9.5963976436418608e-001,
|
||||
9.6000193206808082e-001, 9.6036400887783413e-001, 9.6072599483905807e-001, 9.6108788999733064e-001,
|
||||
9.6144969439819583e-001, 9.6181140808716326e-001, 9.6217303110970820e-001, 9.6253456351127165e-001,
|
||||
9.6289600533726050e-001, 9.6325735663304790e-001, 9.6361861744397259e-001, 9.6397978781533911e-001,
|
||||
9.6434086779241834e-001, 9.6470185742044690e-001, 9.6506275674462760e-001, 9.6542356581012922e-001,
|
||||
9.6578428466208699e-001, 9.6614491334560193e-001, 9.6650545190574111e-001, 9.6686590038753850e-001,
|
||||
9.6722625883599356e-001, 9.6758652729607242e-001, 9.6794670581270770e-001, 9.6830679443079792e-001,
|
||||
9.6866679319520843e-001, 9.6902670215077058e-001, 9.6938652134228265e-001, 9.6974625081450894e-001,
|
||||
9.7010589061218078e-001, 9.7046544077999519e-001, 9.7082490136261712e-001, 9.7118427240467686e-001,
|
||||
9.7154355395077185e-001, 9.7190274604546667e-001, 9.7226184873329191e-001, 9.7262086205874509e-001,
|
||||
9.7297978606629087e-001, 9.7333862080036049e-001, 9.7369736630535186e-001, 9.7405602262563018e-001,
|
||||
9.7441458980552709e-001, 9.7477306788934170e-001, 9.7513145692133973e-001, 9.7548975694575402e-001,
|
||||
9.7584796800678464e-001, 9.7620609014859849e-001, 9.7656412341532972e-001, 9.7692206785107960e-001,
|
||||
9.7727992349991655e-001, 9.7763769040587623e-001, 9.7799536861296144e-001, 9.7835295816514267e-001,
|
||||
9.7871045910635723e-001, 9.7906787148051000e-001, 9.7942519533147321e-001, 9.7978243070308657e-001,
|
||||
9.8013957763915704e-001, 9.8049663618345917e-001, 9.8085360637973529e-001, 9.8121048827169477e-001,
|
||||
9.8156728190301468e-001, 9.8192398731733999e-001, 9.8228060455828281e-001, 9.8263713366942373e-001,
|
||||
9.8299357469431004e-001, 9.8334992767645768e-001, 9.8370619265934944e-001, 9.8406236968643668e-001,
|
||||
9.8441845880113821e-001, 9.8477446004684077e-001, 9.8513037346689891e-001, 9.8548619910463542e-001,
|
||||
9.8584193700334044e-001, 9.8619758720627293e-001, 9.8655314975665886e-001, 9.8690862469769336e-001,
|
||||
9.8726401207253855e-001, 9.8761931192432550e-001, 9.8797452429615296e-001, 9.8832964923108790e-001,
|
||||
9.8868468677216581e-001, 9.8903963696239017e-001, 9.8939449984473249e-001, 9.8974927546213298e-001,
|
||||
9.9010396385750010e-001, 9.9045856507371044e-001, 9.9081307915360939e-001, 9.9116750614001037e-001,
|
||||
9.9152184607569527e-001, 9.9187609900341478e-001, 9.9223026496588806e-001, 9.9258434400580220e-001,
|
||||
9.9293833616581384e-001, 9.9329224148854744e-001, 9.9364606001659650e-001, 9.9399979179252318e-001,
|
||||
9.9435343685885791e-001, 9.9470699525810047e-001, 9.9506046703271911e-001, 9.9541385222515089e-001,
|
||||
9.9576715087780143e-001, 9.9612036303304574e-001, 9.9647348873322728e-001, 9.9682652802065863e-001,
|
||||
9.9717948093762132e-001, 9.9753234752636577e-001, 9.9788512782911110e-001, 9.9823782188804633e-001,
|
||||
9.9859042974532852e-001, 9.9894295144308476e-001, 9.9929538702341059e-001, 9.9964773652837102e-001};
|
||||
|
||||
|
||||
inline double MemLog::mlog10 (double val)
|
||||
{
|
||||
uint64_t* pin = (uint64_t*)(&val);
|
||||
uint64_t N = *pin;
|
||||
int e = (int)(((N >> 52) & 2047) - 1023);
|
||||
int m = (int)((N >> (52 - mbits)) & mmask);
|
||||
return mconv * (e + mtable[m]);
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
49
wdsp/meterlog10.hpp
Normal file
49
wdsp/meterlog10.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
/* meterlog10.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_meterlog10_h
|
||||
#define wdsp_meterlog10_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API MemLog
|
||||
{
|
||||
public:
|
||||
static double mlog10 (double val);
|
||||
|
||||
private:
|
||||
static const int mbits;
|
||||
static const int mmask;
|
||||
static const double mconv;
|
||||
static const double mtable[2048];
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
684
wdsp/nbp.cpp
Normal file
684
wdsp/nbp.cpp
Normal file
@ -0,0 +1,684 @@
|
||||
/* nbp.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "fir.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "snb.hpp"
|
||||
#include "nbp.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Notch Database *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
NOTCHDB* NOTCHDB::create_notchdb (int master_run, int maxnotches)
|
||||
{
|
||||
NOTCHDB *a = new NOTCHDB;
|
||||
a->master_run = master_run;
|
||||
a->maxnotches = maxnotches;
|
||||
a->nn = 0;
|
||||
a->fcenter = new double[a->maxnotches]; // (double *) malloc0 (a->maxnotches * sizeof (double));
|
||||
a->fwidth = new double[a->maxnotches]; // (double *) malloc0 (a->maxnotches * sizeof (double));
|
||||
a->nlow = new double[a->maxnotches]; // (double *) malloc0 (a->maxnotches * sizeof (double));
|
||||
a->nhigh = new double[a->maxnotches]; // (double *) malloc0 (a->maxnotches * sizeof (double));
|
||||
a->active = new int[a->maxnotches]; // (int *) malloc0 (a->maxnotches * sizeof (int ));
|
||||
return a;
|
||||
}
|
||||
|
||||
void NOTCHDB::destroy_notchdb (NOTCHDB *b)
|
||||
{
|
||||
delete[] (b->active);
|
||||
delete[] (b->nhigh);
|
||||
delete[] (b->nlow);
|
||||
delete[] (b->fwidth);
|
||||
delete[] (b->fcenter);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Notched Bandpass Filter *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
double* NBP::fir_mbandpass (int N, int nbp, double* flow, double* fhigh, double rate, double scale, int wintype)
|
||||
{
|
||||
int i, k;
|
||||
double* impulse = new double[N * 2]; // (double *) malloc0 (N * sizeof (complex));
|
||||
double* imp;
|
||||
for (k = 0; k < nbp; k++)
|
||||
{
|
||||
imp = FIR::fir_bandpass (N, flow[k], fhigh[k], rate, wintype, 1, scale);
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
impulse[2 * i + 0] += imp[2 * i + 0];
|
||||
impulse[2 * i + 1] += imp[2 * i + 1];
|
||||
}
|
||||
delete[] (imp);
|
||||
}
|
||||
return impulse;
|
||||
}
|
||||
|
||||
double NBP::min_notch_width (NBP *a)
|
||||
{
|
||||
double min_width;
|
||||
switch (a->wintype)
|
||||
{
|
||||
case 0:
|
||||
min_width = 1600.0 / (a->nc / 256) * (a->rate / 48000);
|
||||
break;
|
||||
case 1:
|
||||
min_width = 2200.0 / (a->nc / 256) * (a->rate / 48000);
|
||||
break;
|
||||
}
|
||||
return min_width;
|
||||
}
|
||||
|
||||
int NBP::make_nbp (
|
||||
int nn,
|
||||
int* active,
|
||||
double* center,
|
||||
double* width,
|
||||
double* nlow,
|
||||
double* nhigh,
|
||||
double minwidth,
|
||||
int autoincr,
|
||||
double flow,
|
||||
double fhigh,
|
||||
double* bplow,
|
||||
double* bphigh,
|
||||
int* havnotch
|
||||
)
|
||||
{
|
||||
int nbp;
|
||||
int nnbp, adds;
|
||||
int i, j, k;
|
||||
double nl, nh;
|
||||
int* del = new int[1024]; // (int *) malloc0 (1024 * sizeof (int));
|
||||
if (fhigh > flow)
|
||||
{
|
||||
bplow[0] = flow;
|
||||
bphigh[0] = fhigh;
|
||||
nbp = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nbp = 0;
|
||||
return nbp;
|
||||
}
|
||||
*havnotch = 0;
|
||||
for (k = 0; k < nn; k++)
|
||||
{
|
||||
if (autoincr && width[k] < minwidth)
|
||||
{
|
||||
nl = center[k] - 0.5 * minwidth;
|
||||
nh = center[k] + 0.5 * minwidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
nl = nlow[k];
|
||||
nh = nhigh[k];
|
||||
}
|
||||
if (active[k] && (nh > flow && nl < fhigh))
|
||||
{
|
||||
*havnotch = 1;
|
||||
adds = 0;
|
||||
for (i = 0; i < nbp; i++)
|
||||
{
|
||||
if (nh > bplow[i] && nl < bphigh[i])
|
||||
{
|
||||
if (nl <= bplow[i] && nh >= bphigh[i])
|
||||
{
|
||||
del[i] = 1;
|
||||
}
|
||||
else if (nl > bplow[i] && nh < bphigh[i])
|
||||
{
|
||||
|
||||
bplow[nbp + adds] = nh;
|
||||
bphigh[nbp + adds] = bphigh[i];
|
||||
bphigh[i] = nl;
|
||||
adds++;
|
||||
}
|
||||
else if (nl <= bplow[i] && nh > bplow[i])
|
||||
{
|
||||
bplow[i] = nh;
|
||||
}
|
||||
else if (nl < bphigh[i] && nh >= bphigh[i])
|
||||
{
|
||||
bphigh[i] = nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
nbp += adds;
|
||||
nnbp = nbp;
|
||||
for (i = 0; i < nbp; i++)
|
||||
{
|
||||
if (del[i] == 1)
|
||||
{
|
||||
nnbp--;
|
||||
for (j = i; j < nnbp; j++)
|
||||
{
|
||||
bplow[j] = bplow[j + 1];
|
||||
bphigh[j] = bphigh[j + 1];
|
||||
}
|
||||
del[i] = 0;
|
||||
}
|
||||
}
|
||||
nbp = nnbp;
|
||||
}
|
||||
}
|
||||
delete[] (del);
|
||||
return nbp;
|
||||
}
|
||||
|
||||
void NBP::calc_nbp_lightweight (NBP *a)
|
||||
{ // calculate and set new impulse response; used when changing tune freq or shift freq
|
||||
int i;
|
||||
double fl, fh;
|
||||
double offset;
|
||||
NOTCHDB *b = a->ptraddr;
|
||||
if (a->fnfrun)
|
||||
{
|
||||
offset = b->tunefreq + b->shift;
|
||||
fl = a->flow + offset;
|
||||
fh = a->fhigh + offset;
|
||||
a->numpb = make_nbp (
|
||||
b->nn,
|
||||
b->active,
|
||||
b->fcenter,
|
||||
b->fwidth,
|
||||
b->nlow,
|
||||
b->nhigh,
|
||||
min_notch_width (a),
|
||||
a->autoincr,
|
||||
fl,
|
||||
fh,
|
||||
a->bplow,
|
||||
a->bphigh,
|
||||
&a->havnotch
|
||||
);
|
||||
// when tuning, no need to recalc filter if there were not and are not any notches in passband
|
||||
if (a->hadnotch || a->havnotch)
|
||||
{
|
||||
for (i = 0; i < a->numpb; i++)
|
||||
{
|
||||
a->bplow[i] -= offset;
|
||||
a->bphigh[i] -= offset;
|
||||
}
|
||||
a->impulse = fir_mbandpass (a->nc, a->numpb, a->bplow, a->bphigh,
|
||||
a->rate, a->gain / (double)(2 * a->size), a->wintype);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
// print_impulse ("nbp.txt", a->size + 1, impulse, 1, 0);
|
||||
delete[](a->impulse);
|
||||
}
|
||||
a->hadnotch = a->havnotch;
|
||||
}
|
||||
else
|
||||
a->hadnotch = 1;
|
||||
}
|
||||
|
||||
void NBP::calc_nbp_impulse (NBP *a)
|
||||
{ // calculates impulse response; for create_fircore() and parameter changes
|
||||
int i;
|
||||
double fl, fh;
|
||||
double offset;
|
||||
NOTCHDB *b = a->ptraddr;
|
||||
if (a->fnfrun)
|
||||
{
|
||||
offset = b->tunefreq + b->shift;
|
||||
fl = a->flow + offset;
|
||||
fh = a->fhigh + offset;
|
||||
a->numpb = make_nbp (
|
||||
b->nn,
|
||||
b->active,
|
||||
b->fcenter,
|
||||
b->fwidth,
|
||||
b->nlow,
|
||||
b->nhigh,
|
||||
min_notch_width (a),
|
||||
a->autoincr,
|
||||
fl,
|
||||
fh,
|
||||
a->bplow,
|
||||
a->bphigh,
|
||||
&a->havnotch
|
||||
);
|
||||
for (i = 0; i < a->numpb; i++)
|
||||
{
|
||||
a->bplow[i] -= offset;
|
||||
a->bphigh[i] -= offset;
|
||||
}
|
||||
a->impulse = fir_mbandpass (
|
||||
a->nc,
|
||||
a->numpb,
|
||||
a->bplow,
|
||||
a->bphigh,
|
||||
a->rate,
|
||||
a->gain / (double)(2 * a->size),
|
||||
a->wintype
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
a->impulse = FIR::fir_bandpass(
|
||||
a->nc,
|
||||
a->flow,
|
||||
a->fhigh,
|
||||
a->rate,
|
||||
a->wintype,
|
||||
1,
|
||||
a->gain / (double)(2 * a->size)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NBP* NBP::create_nbp(
|
||||
int run,
|
||||
int fnfrun,
|
||||
int position,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* out,
|
||||
double flow,
|
||||
double fhigh,
|
||||
int rate,
|
||||
int wintype,
|
||||
double gain,
|
||||
int autoincr,
|
||||
int maxpb,
|
||||
NOTCHDB* ptraddr
|
||||
)
|
||||
{
|
||||
NBP *a = new NBP;
|
||||
a->run = run;
|
||||
a->fnfrun = fnfrun;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->nc = nc;
|
||||
a->mp = mp;
|
||||
a->rate = (double)rate;
|
||||
a->wintype = wintype;
|
||||
a->gain = gain;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->autoincr = autoincr;
|
||||
a->flow = flow;
|
||||
a->fhigh = fhigh;
|
||||
a->maxpb = maxpb;
|
||||
a->ptraddr = ptraddr;
|
||||
a->bplow = new double[a->maxpb]; // (double *) malloc0 (a->maxpb * sizeof (double));
|
||||
a->bphigh = new double[a->maxpb]; // (double *) malloc0 (a->maxpb * sizeof (double));
|
||||
calc_nbp_impulse (a);
|
||||
a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, a->impulse);
|
||||
// print_impulse ("nbp.txt", a->size + 1, impulse, 1, 0);
|
||||
delete[](a->impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void NBP::destroy_nbp (NBP *a)
|
||||
{
|
||||
FIRCORE::destroy_fircore (a->p);
|
||||
delete[] (a->bphigh);
|
||||
delete[] (a->bplow);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void NBP::flush_nbp (NBP *a)
|
||||
{
|
||||
FIRCORE::flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void NBP::xnbp (NBP *a, int pos)
|
||||
{
|
||||
if (a->run && pos == a->position)
|
||||
FIRCORE::xfircore (a->p);
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void NBP::setBuffers_nbp (NBP *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
FIRCORE::setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void NBP::setSamplerate_nbp (NBP *a, int rate)
|
||||
{
|
||||
a->rate = rate;
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
|
||||
void NBP::setSize_nbp (NBP *a, int size)
|
||||
{
|
||||
// NOTE: 'size' must be <= 'nc'
|
||||
a->size = size;
|
||||
FIRCORE::setSize_fircore (a->p, a->size);
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
|
||||
void NBP::setNc_nbp (NBP *a)
|
||||
{
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setNc_fircore (a->p, a->nc, a->impulse);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
|
||||
void NBP::setMp_nbp (NBP *a)
|
||||
{
|
||||
FIRCORE::setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// DATABASE PROPERTIES
|
||||
|
||||
void NBP::UpdateNBPFiltersLightWeight (RXA& rxa)
|
||||
{ // called when setting tune freq or shift freq
|
||||
calc_nbp_lightweight (rxa.nbp0.p);
|
||||
calc_nbp_lightweight (rxa.bpsnba.p->bpsnba);
|
||||
}
|
||||
|
||||
void NBP::UpdateNBPFilters(RXA& rxa)
|
||||
{
|
||||
NBP *a = rxa.nbp0.p;
|
||||
BPSNBA *b = rxa.bpsnba.p;
|
||||
if (a->fnfrun)
|
||||
{
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
if (b->bpsnba->fnfrun)
|
||||
{
|
||||
BPSNBA::recalc_bpsnba_filter (b, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int NBP::NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active)
|
||||
{
|
||||
NOTCHDB *b;
|
||||
int i, j;
|
||||
int rval;
|
||||
b = rxa.ndb.p;
|
||||
if (notch <= b->nn && b->nn < b->maxnotches)
|
||||
{
|
||||
b->nn++;
|
||||
for (i = b->nn - 2, j = b->nn - 1; i >= notch; i--, j--)
|
||||
{
|
||||
b->fcenter[j] = b->fcenter[i];
|
||||
b->fwidth[j] = b->fwidth[i];
|
||||
b->nlow[j] = b->nlow[i];
|
||||
b->nhigh[j] = b->nhigh[i];
|
||||
b->active[j] = b->active[i];
|
||||
}
|
||||
b->fcenter[notch] = fcenter;
|
||||
b->fwidth[notch] = fwidth;
|
||||
b->nlow[notch] = fcenter - 0.5 * fwidth;
|
||||
b->nhigh[notch] = fcenter + 0.5 * fwidth;
|
||||
b->active[notch] = active;
|
||||
UpdateNBPFilters (rxa);
|
||||
rval = 0;
|
||||
}
|
||||
else
|
||||
rval = -1;
|
||||
return rval;
|
||||
}
|
||||
|
||||
int NBP::NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active)
|
||||
{
|
||||
NOTCHDB *a;
|
||||
int rval;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.ndb.p;
|
||||
if (notch < a->nn)
|
||||
{
|
||||
*fcenter = a->fcenter[notch];
|
||||
*fwidth = a->fwidth[notch];
|
||||
*active = a->active[notch];
|
||||
rval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*fcenter = -1.0;
|
||||
*fwidth = 0.0;
|
||||
*active = -1;
|
||||
rval = -1;
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
return rval;
|
||||
}
|
||||
|
||||
int NBP::NBPDeleteNotch (RXA& rxa, int notch)
|
||||
{
|
||||
int i, j;
|
||||
int rval;
|
||||
NOTCHDB *a;
|
||||
a = rxa.ndb.p;
|
||||
if (notch < a->nn)
|
||||
{
|
||||
a->nn--;
|
||||
for (i = notch, j = notch + 1; i < a->nn; i++, j++)
|
||||
{
|
||||
a->fcenter[i] = a->fcenter[j];
|
||||
a->fwidth[i] = a->fwidth[j];
|
||||
a->nlow[i] = a->nlow[j];
|
||||
a->nhigh[i] = a->nhigh[j];
|
||||
a->active[i] = a->active[j];
|
||||
}
|
||||
UpdateNBPFilters (rxa);
|
||||
rval = 0;
|
||||
}
|
||||
else
|
||||
rval = -1;
|
||||
return rval;
|
||||
}
|
||||
|
||||
int NBP::NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active)
|
||||
{
|
||||
NOTCHDB *a;
|
||||
int rval;
|
||||
a = rxa.ndb.p;
|
||||
if (notch < a->nn)
|
||||
{
|
||||
a->fcenter[notch] = fcenter;
|
||||
a->fwidth[notch] = fwidth;
|
||||
a->nlow[notch] = fcenter - 0.5 * fwidth;
|
||||
a->nhigh[notch] = fcenter + 0.5 * fwidth;
|
||||
a->active[notch] = active;
|
||||
UpdateNBPFilters (rxa);
|
||||
rval = 0;
|
||||
}
|
||||
else
|
||||
rval = -1;
|
||||
return rval;
|
||||
}
|
||||
|
||||
void NBP::NBPGetNumNotches (RXA& rxa, int* nnotches)
|
||||
{
|
||||
NOTCHDB *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.ndb.p;
|
||||
*nnotches = a->nn;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void NBP::NBPSetTuneFrequency (RXA& rxa, double tunefreq)
|
||||
{
|
||||
NOTCHDB *a;
|
||||
a = rxa.ndb.p;
|
||||
if (tunefreq != a->tunefreq)
|
||||
{
|
||||
a->tunefreq = tunefreq;
|
||||
UpdateNBPFiltersLightWeight (rxa);
|
||||
}
|
||||
}
|
||||
|
||||
void NBP::NBPSetShiftFrequency (RXA& rxa, double shift)
|
||||
{
|
||||
NOTCHDB *a;
|
||||
a = rxa.ndb.p;
|
||||
if (shift != a->shift)
|
||||
{
|
||||
a->shift = shift;
|
||||
UpdateNBPFiltersLightWeight (rxa);
|
||||
}
|
||||
}
|
||||
|
||||
void NBP::NBPSetNotchesRun (RXA& rxa, int run)
|
||||
{
|
||||
NOTCHDB *a = rxa.ndb.p;
|
||||
NBP *b = rxa.nbp0.p;
|
||||
if ( run != a->master_run)
|
||||
{
|
||||
a->master_run = run; // update variables
|
||||
b->fnfrun = a->master_run;
|
||||
BPSNBA::bpsnbaCheck (rxa, rxa.mode, run);
|
||||
calc_nbp_impulse (b); // recalc nbp impulse response
|
||||
FIRCORE::setImpulse_fircore (b->p, b->impulse, 0); // calculate new filter masks
|
||||
delete[] (b->impulse);
|
||||
rxa.csDSP.lock(); // block DSP channel processing
|
||||
BPSNBA::bpsnbaSet (rxa);
|
||||
FIRCORE::setUpdate_fircore (b->p); // apply new filter masks
|
||||
rxa.csDSP.unlock(); // unblock channel processing
|
||||
}
|
||||
}
|
||||
|
||||
// FILTER PROPERTIES
|
||||
|
||||
void NBP::NBPSetRun (RXA& rxa, int run)
|
||||
{
|
||||
NBP *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.nbp0.p;
|
||||
a->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void NBP::NBPSetFreqs (RXA& rxa, double flow, double fhigh)
|
||||
{
|
||||
NBP *a;
|
||||
a = rxa.nbp0.p;
|
||||
if ((flow != a->flow) || (fhigh != a->fhigh))
|
||||
{
|
||||
a->flow = flow;
|
||||
a->fhigh = fhigh;
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
}
|
||||
|
||||
void NBP::NBPSetWindow (RXA& rxa, int wintype)
|
||||
{
|
||||
NBP *a;
|
||||
BPSNBA *b;
|
||||
a = rxa.nbp0.p;
|
||||
b = rxa.bpsnba.p;
|
||||
if ((a->wintype != wintype))
|
||||
{
|
||||
a->wintype = wintype;
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
if ((b->wintype != wintype))
|
||||
{
|
||||
b->wintype = wintype;
|
||||
BPSNBA::recalc_bpsnba_filter (b, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void NBP::NBPSetNC (RXA& rxa, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
NBP *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.nbp0.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
setNc_nbp (a);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void NBP::NBPSetMP (RXA& rxa, int mp)
|
||||
{
|
||||
NBP *a;
|
||||
a = rxa.nbp0.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_nbp (a);
|
||||
}
|
||||
}
|
||||
|
||||
void NBP::NBPGetMinNotchWidth (RXA& rxa, double* minwidth)
|
||||
{
|
||||
NBP *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.nbp0.p;
|
||||
*minwidth = min_notch_width (a);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void NBP::NBPSetAutoIncrease (RXA& rxa, int autoincr)
|
||||
{
|
||||
NBP *a;
|
||||
BPSNBA *b;
|
||||
a = rxa.nbp0.p;
|
||||
b = rxa.bpsnba.p;
|
||||
if ((a->autoincr != autoincr))
|
||||
{
|
||||
a->autoincr = autoincr;
|
||||
calc_nbp_impulse (a);
|
||||
FIRCORE::setImpulse_fircore (a->p, a->impulse, 1);
|
||||
delete[] (a->impulse);
|
||||
}
|
||||
if ((b->autoincr != autoincr))
|
||||
{
|
||||
b->autoincr = autoincr;
|
||||
BPSNBA::recalc_bpsnba_filter (b, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
155
wdsp/nbp.hpp
Normal file
155
wdsp/nbp.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
/* nbp.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_nbp_h
|
||||
#define wdsp_nbp_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class FIRCORE;
|
||||
class RXA;
|
||||
|
||||
class WDSP_API NOTCHDB
|
||||
{
|
||||
public:
|
||||
int master_run;
|
||||
double tunefreq;
|
||||
double shift;
|
||||
int nn;
|
||||
int* active;
|
||||
double* fcenter;
|
||||
double* fwidth;
|
||||
double* nlow;
|
||||
double* nhigh;
|
||||
int maxnotches;
|
||||
|
||||
static NOTCHDB* create_notchdb (int master_run, int maxnotches);
|
||||
static void destroy_notchdb (NOTCHDB *b);
|
||||
};
|
||||
|
||||
|
||||
class NBP
|
||||
{
|
||||
public:
|
||||
int run; // run the filter
|
||||
int fnfrun; // use the notches
|
||||
int position; // position in processing pipeline
|
||||
int size; // buffer size
|
||||
int nc; // number of filter coefficients
|
||||
int mp; // minimum phase flag
|
||||
double* in; // input buffer
|
||||
double* out; // output buffer
|
||||
double flow; // low bandpass cutoff freq
|
||||
double fhigh; // high bandpass cutoff freq
|
||||
double* impulse; // filter impulse response
|
||||
double rate; // sample rate
|
||||
int wintype; // filter window type
|
||||
double gain; // filter gain
|
||||
int autoincr; // auto-increment notch width
|
||||
int maxpb; // maximum number of passbands
|
||||
NOTCHDB* ptraddr; // ptr to addr of notch-database data structure
|
||||
double* bplow; // array of passband lows
|
||||
double* bphigh; // array of passband highs
|
||||
int numpb; // number of passbands
|
||||
FIRCORE *p;
|
||||
int havnotch;
|
||||
int hadnotch;
|
||||
|
||||
static NBP* create_nbp(
|
||||
int run,
|
||||
int fnfrun,
|
||||
int position,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* out,
|
||||
double flow,
|
||||
double fhigh,
|
||||
int rate,
|
||||
int wintype,
|
||||
double gain,
|
||||
int autoincr,
|
||||
int maxpb,
|
||||
NOTCHDB* ptraddr
|
||||
);
|
||||
static void destroy_nbp (NBP *a);
|
||||
static void flush_nbp (NBP *a);
|
||||
static void xnbp (NBP *a, int pos);
|
||||
static void setBuffers_nbp (NBP *a, double* in, double* out);
|
||||
static void setSamplerate_nbp (NBP *a, int rate);
|
||||
static void setSize_nbp (NBP *a, int size);
|
||||
static void calc_nbp_impulse (NBP *a);
|
||||
static void setNc_nbp (NBP *a);
|
||||
static void setMp_nbp (NBP *a);
|
||||
// RXA Properties
|
||||
static void UpdateNBPFiltersLightWeight (RXA& rxa);
|
||||
static void UpdateNBPFilters(RXA& rxa);
|
||||
static int NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active);
|
||||
static int NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active);
|
||||
static int NBPDeleteNotch (RXA& rxa, int notch);
|
||||
static int NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active);
|
||||
static void NBPGetNumNotches (RXA& rxa, int* nnotches);
|
||||
static void NBPSetTuneFrequency (RXA& rxa, double tunefreq);
|
||||
static void NBPSetShiftFrequency (RXA& rxa, double shift);
|
||||
static void NBPSetNotchesRun (RXA& rxa, int run);
|
||||
static void NBPSetRun (RXA& rxa, int run);
|
||||
static void NBPSetFreqs (RXA& rxa, double flow, double fhigh);
|
||||
static void NBPSetWindow (RXA& rxa, int wintype);
|
||||
|
||||
static void NBPSetNC (RXA& rxa, int nc);
|
||||
static void NBPSetMP (RXA& rxa, int mp);
|
||||
|
||||
static void NBPGetMinNotchWidth (RXA& rxa, double* minwidth);
|
||||
static void NBPSetAutoIncrease (RXA& rxa, int autoincr);
|
||||
|
||||
private:
|
||||
static double* fir_mbandpass (int N, int nbp, double* flow, double* fhigh, double rate, double scale, int wintype);
|
||||
static double min_notch_width (NBP *a);
|
||||
static int make_nbp (
|
||||
int nn,
|
||||
int* active,
|
||||
double* center,
|
||||
double* width,
|
||||
double* nlow,
|
||||
double* nhigh,
|
||||
double minwidth,
|
||||
int autoincr,
|
||||
double flow,
|
||||
double fhigh,
|
||||
double* bplow,
|
||||
double* bphigh,
|
||||
int* havnotch
|
||||
);
|
||||
static void calc_nbp_lightweight (NBP *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
158
wdsp/osctrl.cpp
Normal file
158
wdsp/osctrl.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/* osctrl.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 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
|
||||
|
||||
*/
|
||||
|
||||
// This file is part of the implementation of the Overshoot Controller from
|
||||
// "Controlled Envelope Single Sideband" by David L. Hershberger, W9GR, in
|
||||
// the November/December 2014 issue of QEX.
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "osctrl.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void OSCTRL::calc_osctrl (OSCTRL *a)
|
||||
{
|
||||
a->pn = (int)((0.3 / a->bw) * a->rate + 0.5);
|
||||
if ((a->pn & 1) == 0) a->pn += 1;
|
||||
if (a->pn < 3) a->pn = 3;
|
||||
a->dl_len = a->pn >> 1;
|
||||
a->dl = new double[a->pn * 2]; // (double *) malloc0 (a->pn * sizeof (complex));
|
||||
a->dlenv = new double[a->pn]; // (double *) malloc0 (a->pn * sizeof (double));
|
||||
a->in_idx = 0;
|
||||
a->out_idx = a->in_idx + a->dl_len;
|
||||
a->max_env = 0.0;
|
||||
}
|
||||
|
||||
void OSCTRL::decalc_osctrl (OSCTRL *a)
|
||||
{
|
||||
delete[] (a->dlenv);
|
||||
delete[] (a->dl);
|
||||
}
|
||||
|
||||
OSCTRL* OSCTRL::create_osctrl (
|
||||
int run,
|
||||
int size,
|
||||
double* inbuff,
|
||||
double* outbuff,
|
||||
int rate,
|
||||
double osgain
|
||||
)
|
||||
{
|
||||
OSCTRL *a = new OSCTRL;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->inbuff = inbuff;
|
||||
a->outbuff = outbuff;
|
||||
a->rate = rate;
|
||||
a->osgain = osgain;
|
||||
a->bw = 3000.0;
|
||||
calc_osctrl (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void OSCTRL::destroy_osctrl (OSCTRL *a)
|
||||
{
|
||||
decalc_osctrl (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void OSCTRL::flush_osctrl (OSCTRL *a)
|
||||
{
|
||||
memset (a->dl, 0, a->dl_len * sizeof (dcomplex));
|
||||
memset (a->dlenv, 0, a->pn * sizeof (double));
|
||||
}
|
||||
|
||||
void OSCTRL::xosctrl (OSCTRL *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i, j;
|
||||
double divisor;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->dl[2 * a->in_idx + 0] = a->inbuff[2 * i + 0]; // put sample in delay line
|
||||
a->dl[2 * a->in_idx + 1] = a->inbuff[2 * i + 1];
|
||||
a->env_out = a->dlenv[a->in_idx]; // take env out of delay line
|
||||
a->dlenv[a->in_idx] = sqrt (a->inbuff[2 * i + 0] * a->inbuff[2 * i + 0] // put env in delay line
|
||||
+ a->inbuff[2 * i + 1] * a->inbuff[2 * i + 1]);
|
||||
if (a->dlenv[a->in_idx] > a->max_env) a->max_env = a->dlenv[a->in_idx];
|
||||
if (a->env_out >= a->max_env && a->env_out > 0.0) // run the buffer
|
||||
{
|
||||
a->max_env = 0.0;
|
||||
for (j = 0; j < a->pn; j++)
|
||||
if (a->dlenv[j] > a->max_env) a->max_env = a->dlenv[j];
|
||||
}
|
||||
if (a->max_env > 1.0) divisor = 1.0 + a->osgain * (a->max_env - 1.0);
|
||||
else divisor = 1.0;
|
||||
a->outbuff[2 * i + 0] = a->dl[2 * a->out_idx + 0] / divisor; // output sample
|
||||
a->outbuff[2 * i + 1] = a->dl[2 * a->out_idx + 1] / divisor;
|
||||
if (--a->in_idx < 0) a->in_idx += a->pn;
|
||||
if (--a->out_idx < 0) a->out_idx += a->pn;
|
||||
}
|
||||
}
|
||||
else if (a->inbuff != a->outbuff)
|
||||
memcpy (a->outbuff, a->inbuff, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void OSCTRL::setBuffers_osctrl (OSCTRL *a, double* in, double* out)
|
||||
{
|
||||
a->inbuff = in;
|
||||
a->outbuff = out;
|
||||
}
|
||||
|
||||
void OSCTRL::setSamplerate_osctrl (OSCTRL *a, int rate)
|
||||
{
|
||||
decalc_osctrl (a);
|
||||
a->rate = rate;
|
||||
calc_osctrl (a);
|
||||
}
|
||||
|
||||
void OSCTRL::setSize_osctrl (OSCTRL *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_osctrl (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void OSCTRL::SetosctrlRun (TXA& txa, int run)
|
||||
{
|
||||
if (txa.osctrl.p->run != run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.osctrl.p->run = run;
|
||||
TXA::SetupBPFilters (txa);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
84
wdsp/osctrl.hpp
Normal file
84
wdsp/osctrl.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
/* osctrl.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014 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
|
||||
|
||||
*/
|
||||
|
||||
// This file is part of the implementation of the Overshoot Controller from
|
||||
// "Controlled Envelope Single Sideband" by David L. Hershberger, W9GR, in
|
||||
// the November/December 2014 issue of QEX.
|
||||
|
||||
#ifndef wdsp_osctrl_h
|
||||
#define wdsp_osctrl_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API OSCTRL
|
||||
{
|
||||
public:
|
||||
int run; // 1 to run; 0 otherwise
|
||||
int size; // buffer size
|
||||
double *inbuff; // input buffer
|
||||
double *outbuff; // output buffer
|
||||
int rate; // sample rate
|
||||
double osgain; // gain applied to overshoot "clippings"
|
||||
double bw; // bandwidth
|
||||
int pn; // "peak stretcher" window, samples
|
||||
int dl_len; // delay line length, samples
|
||||
double* dl; // delay line for complex samples
|
||||
double* dlenv; // delay line for envelope values
|
||||
int in_idx; // input index for dl
|
||||
int out_idx; // output index for dl
|
||||
double max_env; // maximum env value in env delay line
|
||||
double env_out;
|
||||
|
||||
static void xosctrl (OSCTRL *a);
|
||||
static OSCTRL* create_osctrl (
|
||||
int run,
|
||||
int size,
|
||||
double* inbuff,
|
||||
double* outbuff,
|
||||
int rate,
|
||||
double osgain
|
||||
);
|
||||
static void destroy_osctrl (OSCTRL *a);
|
||||
static void flush_osctrl (OSCTRL *a);
|
||||
static void setBuffers_osctrl (OSCTRL *a, double* in, double* out);
|
||||
static void setSamplerate_osctrl (OSCTRL *a, int rate);
|
||||
static void setSize_osctrl (OSCTRL *a, int size);
|
||||
// TXA Properties
|
||||
static void SetosctrlRun (TXA& txa, int run);
|
||||
|
||||
private:
|
||||
static void calc_osctrl (OSCTRL *a);
|
||||
static void decalc_osctrl (OSCTRL *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
224
wdsp/patchpanel.cpp
Normal file
224
wdsp/patchpanel.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
/* patchpanel.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "patchpanel.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
PANEL* PANEL::create_panel (int run, int size, double* in, double* out, double gain1, double gain2I, double gain2Q, int inselect, int copy)
|
||||
{
|
||||
PANEL* a = new PANEL;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->gain1 = gain1;
|
||||
a->gain2I = gain2I;
|
||||
a->gain2Q = gain2Q;
|
||||
a->inselect = inselect;
|
||||
a->copy = copy;
|
||||
return a;
|
||||
}
|
||||
|
||||
void PANEL::destroy_panel (PANEL *a)
|
||||
{
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void PANEL::flush_panel (PANEL *)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PANEL::xpanel (PANEL *a)
|
||||
{
|
||||
int i;
|
||||
double I, Q;
|
||||
double gainI = a->gain1 * a->gain2I;
|
||||
double gainQ = a->gain1 * a->gain2Q;
|
||||
// inselect is either 0(neither), 1(Q), 2(I), or 3(both)
|
||||
switch (a->copy)
|
||||
{
|
||||
case 0: // no copy
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
I = a->in[2 * i + 0] * (a->inselect >> 1);
|
||||
Q = a->in[2 * i + 1] * (a->inselect & 1);
|
||||
a->out[2 * i + 0] = gainI * I;
|
||||
a->out[2 * i + 1] = gainQ * Q;
|
||||
}
|
||||
break;
|
||||
case 1: // copy I to Q (then Q == I)
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
I = a->in[2 * i + 0] * (a->inselect >> 1);
|
||||
Q = I;
|
||||
a->out[2 * i + 0] = gainI * I;
|
||||
a->out[2 * i + 1] = gainQ * Q;
|
||||
}
|
||||
break;
|
||||
case 2: // copy Q to I (then I == Q)
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
Q = a->in[2 * i + 1] * (a->inselect & 1);
|
||||
I = Q;
|
||||
a->out[2 * i + 0] = gainI * I;
|
||||
a->out[2 * i + 1] = gainQ * Q;
|
||||
}
|
||||
break;
|
||||
case 3: // reverse (I=>Q and Q=>I)
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
Q = a->in[2 * i + 0] * (a->inselect >> 1);
|
||||
I = a->in[2 * i + 1] * (a->inselect & 1);
|
||||
a->out[2 * i + 0] = gainI * I;
|
||||
a->out[2 * i + 1] = gainQ * Q;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PANEL::setBuffers_panel (PANEL *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void PANEL::setSamplerate_panel (PANEL *, int)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PANEL::setSize_panel (PANEL *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void PANEL::SetPanelRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.panel.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelSelect (RXA& rxa, int select)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.panel.p->inselect = select;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelGain1 (RXA& rxa, double gain)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.panel.p->gain1 = gain;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelGain2 (RXA& rxa, double gainI, double gainQ)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.panel.p->gain2I = gainI;
|
||||
rxa.panel.p->gain2Q = gainQ;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelPan (RXA& rxa, double pan)
|
||||
{
|
||||
double gain1, gain2;
|
||||
rxa.csDSP.lock();
|
||||
if (pan <= 0.5)
|
||||
{
|
||||
gain1 = 1.0;
|
||||
gain2 = sin (pan * PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
gain1 = sin (pan * PI);
|
||||
gain2 = 1.0;
|
||||
}
|
||||
rxa.panel.p->gain2I = gain1;
|
||||
rxa.panel.p->gain2Q = gain2;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelCopy (RXA& rxa, int copy)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.panel.p->copy = copy;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelBinaural (RXA& rxa, int bin)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.panel.p->copy = 1 - bin;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void PANEL::SetPanelRun (TXA& txa, int run)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.panel.p->run = run;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelGain1 (TXA& txa, double gain)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
txa.panel.p->gain1 = gain;
|
||||
//print_message ("micgainset.txt", "Set MIC Gain to", (int)(100.0 * gain), 0, 0);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void PANEL::SetPanelSelect (TXA& txa, int select)
|
||||
{
|
||||
txa.csDSP.lock();
|
||||
if (select == 1)
|
||||
txa.panel.p->copy = 3;
|
||||
else
|
||||
txa.panel.p->copy = 0;
|
||||
txa.panel.p->inselect = select;
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
74
wdsp/patchpanel.hpp
Normal file
74
wdsp/patchpanel.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
/* patchpanel.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_patchpanel_h
|
||||
#define wdsp_patchpanel_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API PANEL
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double gain1;
|
||||
double gain2I;
|
||||
double gain2Q;
|
||||
int inselect;
|
||||
int copy;
|
||||
|
||||
static PANEL* create_panel (int run, int size, double* in, double* out, double gain1, double gain2I, double gain2Q, int inselect, int copy);
|
||||
static void destroy_panel (PANEL *a);
|
||||
static void flush_panel (PANEL *a);
|
||||
static void xpanel (PANEL *a);
|
||||
static void setBuffers_panel (PANEL *a, double* in, double* out);
|
||||
static void setSamplerate_panel (PANEL *a, int rate);
|
||||
static void setSize_panel (PANEL *a, int size);
|
||||
// RXA Properties
|
||||
static void SetPanelRun (RXA& rxa, int run);
|
||||
static void SetPanelSelect (RXA& rxa, int select);
|
||||
static void SetPanelGain1 (RXA& rxa, double gain);
|
||||
static void SetPanelGain2 (RXA& rxa, double gainI, double gainQ);
|
||||
static void SetPanelPan (RXA& rxa, double pan);
|
||||
static void SetPanelCopy (RXA& rxa, int copy);
|
||||
static void SetPanelBinaural (RXA& rxa, int bin);
|
||||
// TXA Properties
|
||||
static void SetPanelRun (TXA& txa, int run);
|
||||
static void SetPanelGain1 (TXA& txa, double gain);
|
||||
static void SetPanelSelect (TXA& txa, int select);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
360
wdsp/resample.cpp
Normal file
360
wdsp/resample.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
/* resample.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "fir.hpp"
|
||||
#include "resample.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/************************************************************************************************
|
||||
* *
|
||||
* VERSION FOR COMPLEX DOUBLE-PRECISION *
|
||||
* *
|
||||
************************************************************************************************/
|
||||
|
||||
void RESAMPLE::calc_resample (RESAMPLE *a)
|
||||
{
|
||||
int x, y, z;
|
||||
int i, j, k;
|
||||
int min_rate;
|
||||
double full_rate;
|
||||
double fc_norm_high, fc_norm_low;
|
||||
double* impulse;
|
||||
a->fc = a->fcin;
|
||||
a->ncoef = a->ncoefin;
|
||||
x = a->in_rate;
|
||||
y = a->out_rate;
|
||||
while (y != 0)
|
||||
{
|
||||
z = y;
|
||||
y = x % y;
|
||||
x = z;
|
||||
}
|
||||
a->L = a->out_rate / x;
|
||||
a->M = a->in_rate / x;
|
||||
if (a->in_rate < a->out_rate) min_rate = a->in_rate;
|
||||
else min_rate = a->out_rate;
|
||||
if (a->fc == 0.0) a->fc = 0.45 * (double)min_rate;
|
||||
full_rate = (double)(a->in_rate * a->L);
|
||||
fc_norm_high = a->fc / full_rate;
|
||||
if (a->fc_low < 0.0)
|
||||
fc_norm_low = - fc_norm_high;
|
||||
else
|
||||
fc_norm_low = a->fc_low / full_rate;
|
||||
if (a->ncoef == 0) a->ncoef = (int)(140.0 * full_rate / min_rate);
|
||||
a->ncoef = (a->ncoef / a->L + 1) * a->L;
|
||||
a->cpp = a->ncoef / a->L;
|
||||
a->h = new double[a->ncoef]; // (double *)malloc0(a->ncoef * sizeof(double));
|
||||
impulse = FIR::fir_bandpass(a->ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, a->gain * (double)a->L);
|
||||
i = 0;
|
||||
for (j = 0; j < a->L; j++)
|
||||
for (k = 0; k < a->ncoef; k += a->L)
|
||||
a->h[i++] = impulse[j + k];
|
||||
a->ringsize = a->cpp;
|
||||
a->ring = new double[a->ringsize]; // (double *)malloc0(a->ringsize * sizeof(complex));
|
||||
a->idx_in = a->ringsize - 1;
|
||||
a->phnum = 0;
|
||||
delete[] (impulse);
|
||||
}
|
||||
|
||||
void RESAMPLE::decalc_resample (RESAMPLE *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete[] (a->h);
|
||||
}
|
||||
|
||||
RESAMPLE* RESAMPLE::create_resample ( int run, int size, double* in, double* out, int in_rate, int out_rate, double fc, int ncoef, double gain)
|
||||
{
|
||||
RESAMPLE *a = new RESAMPLE;
|
||||
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->in_rate = in_rate;
|
||||
a->out_rate = out_rate;
|
||||
a->fcin = fc;
|
||||
a->fc_low = -1.0; // could add to create_resample() parameters
|
||||
a->ncoefin = ncoef;
|
||||
a->gain = gain;
|
||||
calc_resample (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
void RESAMPLE::destroy_resample (RESAMPLE *a)
|
||||
{
|
||||
decalc_resample (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
|
||||
void RESAMPLE::flush_resample (RESAMPLE *a)
|
||||
{
|
||||
memset (a->ring, 0, a->ringsize * sizeof (dcomplex));
|
||||
a->idx_in = a->ringsize - 1;
|
||||
a->phnum = 0;
|
||||
}
|
||||
|
||||
|
||||
int RESAMPLE::xresample (RESAMPLE *a)
|
||||
{
|
||||
int outsamps = 0;
|
||||
if (a->run)
|
||||
{
|
||||
int i, j, n;
|
||||
int idx_out;
|
||||
double I, Q;
|
||||
|
||||
for (i = 0; i < a->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];
|
||||
while (a->phnum < a->L)
|
||||
{
|
||||
I = 0.0;
|
||||
Q = 0.0;
|
||||
n = a->cpp * a->phnum;
|
||||
for (j = 0; j < a->cpp; j++)
|
||||
{
|
||||
if ((idx_out = a->idx_in + j) >= a->ringsize) idx_out -= a->ringsize;
|
||||
I += a->h[n + j] * a->ring[2 * idx_out + 0];
|
||||
Q += a->h[n + j] * a->ring[2 * idx_out + 1];
|
||||
}
|
||||
a->out[2 * outsamps + 0] = I;
|
||||
a->out[2 * outsamps + 1] = Q;
|
||||
outsamps++;
|
||||
a->phnum += a->M;
|
||||
}
|
||||
a->phnum -= a->L;
|
||||
if (--a->idx_in < 0) a->idx_in = a->ringsize - 1;
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
return outsamps;
|
||||
}
|
||||
|
||||
void RESAMPLE::setBuffers_resample(RESAMPLE *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void RESAMPLE::setSize_resample(RESAMPLE *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_resample (a);
|
||||
}
|
||||
|
||||
void RESAMPLE::setInRate_resample(RESAMPLE *a, int rate)
|
||||
{
|
||||
decalc_resample (a);
|
||||
a->in_rate = rate;
|
||||
calc_resample (a);
|
||||
}
|
||||
|
||||
void RESAMPLE::setOutRate_resample(RESAMPLE *a, int rate)
|
||||
{
|
||||
decalc_resample (a);
|
||||
a->out_rate = rate;
|
||||
calc_resample (a);
|
||||
}
|
||||
|
||||
void RESAMPLE::setFCLow_resample (RESAMPLE *a, double fc_low)
|
||||
{
|
||||
if (fc_low != a->fc_low)
|
||||
{
|
||||
decalc_resample (a);
|
||||
a->fc_low = fc_low;
|
||||
calc_resample (a);
|
||||
}
|
||||
}
|
||||
|
||||
void RESAMPLE::setBandwidth_resample (RESAMPLE *a, double fc_low, double fc_high)
|
||||
{
|
||||
if (fc_low != a->fc_low || fc_high != a->fcin)
|
||||
{
|
||||
decalc_resample (a);
|
||||
a->fc_low = fc_low;
|
||||
a->fcin = fc_high;
|
||||
calc_resample (a);
|
||||
}
|
||||
}
|
||||
|
||||
// exported calls
|
||||
|
||||
|
||||
void* RESAMPLE::create_resampleV (int in_rate, int out_rate)
|
||||
{
|
||||
return (void *)create_resample (1, 0, 0, 0, in_rate, out_rate, 0.0, 0, 1.0);
|
||||
}
|
||||
|
||||
|
||||
void RESAMPLE::xresampleV (double* input, double* output, int numsamps, int* outsamps, void* ptr)
|
||||
{
|
||||
RESAMPLE *a = (RESAMPLE*) ptr;
|
||||
a->in = input;
|
||||
a->out = output;
|
||||
a->size = numsamps;
|
||||
*outsamps = xresample(a);
|
||||
}
|
||||
|
||||
|
||||
void RESAMPLE::destroy_resampleV (void* ptr)
|
||||
{
|
||||
destroy_resample ( (RESAMPLE*) ptr );
|
||||
}
|
||||
|
||||
/************************************************************************************************
|
||||
* *
|
||||
* VERSION FOR NON-COMPLEX FLOATS *
|
||||
* *
|
||||
************************************************************************************************/
|
||||
|
||||
RESAMPLEF* RESAMPLEF::create_resampleF ( int run, int size, float* in, float* out, int in_rate, int out_rate)
|
||||
{
|
||||
RESAMPLEF *a = new RESAMPLEF;
|
||||
int x, y, z;
|
||||
int i, j, k;
|
||||
int min_rate;
|
||||
double full_rate;
|
||||
double fc;
|
||||
double fc_norm;
|
||||
double* impulse;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
x = in_rate;
|
||||
y = out_rate;
|
||||
while (y != 0)
|
||||
{
|
||||
z = y;
|
||||
y = x % y;
|
||||
x = z;
|
||||
}
|
||||
a->L = out_rate / x;
|
||||
a->M = in_rate / x;
|
||||
if (in_rate < out_rate) min_rate = in_rate;
|
||||
else min_rate = out_rate;
|
||||
fc = 0.45 * (double)min_rate;
|
||||
full_rate = (double)(in_rate * a->L);
|
||||
fc_norm = fc / full_rate;
|
||||
a->ncoef = (int)(60.0 / fc_norm);
|
||||
a->ncoef = (a->ncoef / a->L + 1) * a->L;
|
||||
a->cpp = a->ncoef / a->L;
|
||||
a->h = new double[a->ncoef]; // (double *) malloc0 (a->ncoef * sizeof (double));
|
||||
impulse = FIR::fir_bandpass (a->ncoef, -fc_norm, +fc_norm, 1.0, 1, 0, (double)a->L);
|
||||
i = 0;
|
||||
for (j = 0; j < a->L; j ++)
|
||||
for (k = 0; k < a->ncoef; k += a->L)
|
||||
a->h[i++] = impulse[j + k];
|
||||
a->ringsize = a->cpp;
|
||||
a->ring = new double[a->ringsize]; //(double *) malloc0 (a->ringsize * sizeof (double));
|
||||
a->idx_in = a->ringsize - 1;
|
||||
a->phnum = 0;
|
||||
delete[] (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void RESAMPLEF::destroy_resampleF (RESAMPLEF *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete[] (a->h);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void RESAMPLEF::flush_resampleF (RESAMPLEF *a)
|
||||
{
|
||||
memset (a->ring, 0, a->ringsize * sizeof (double));
|
||||
a->idx_in = a->ringsize - 1;
|
||||
a->phnum = 0;
|
||||
}
|
||||
|
||||
int RESAMPLEF::xresampleF (RESAMPLEF *a)
|
||||
{
|
||||
int outsamps = 0;
|
||||
if (a->run)
|
||||
{
|
||||
int i, j, n;
|
||||
int idx_out;
|
||||
double I;
|
||||
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->ring[a->idx_in] = (double)a->in[i];
|
||||
|
||||
while (a->phnum < a->L)
|
||||
{
|
||||
I = 0.0;
|
||||
n = a->cpp * a->phnum;
|
||||
for (j = 0; j < a->cpp; j++)
|
||||
{
|
||||
if ((idx_out = a->idx_in + j) >= a->ringsize) idx_out -= a->ringsize;
|
||||
I += a->h[n + j] * a->ring[idx_out];
|
||||
}
|
||||
a->out[outsamps] = (float)I;
|
||||
|
||||
outsamps++;
|
||||
a->phnum += a->M;
|
||||
}
|
||||
a->phnum -= a->L;
|
||||
if (--a->idx_in < 0) a->idx_in = a->ringsize - 1;
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (float));
|
||||
return outsamps;
|
||||
}
|
||||
|
||||
// Exported calls
|
||||
|
||||
|
||||
void* RESAMPLEF::create_resampleFV (int in_rate, int out_rate)
|
||||
{
|
||||
return (void *) create_resampleF (1, 0, 0, 0, in_rate, out_rate);
|
||||
}
|
||||
|
||||
|
||||
void RESAMPLEF::xresampleFV (float* input, float* output, int numsamps, int* outsamps, void* ptr)
|
||||
{
|
||||
RESAMPLEF *a = (RESAMPLEF*) ptr;
|
||||
a->in = input;
|
||||
a->out = output;
|
||||
a->size = numsamps;
|
||||
*outsamps = xresampleF(a);
|
||||
}
|
||||
|
||||
|
||||
void RESAMPLEF::destroy_resampleFV (void* ptr)
|
||||
{
|
||||
destroy_resampleF ( (RESAMPLEF*) ptr );
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
127
wdsp/resample.hpp
Normal file
127
wdsp/resample.hpp
Normal file
@ -0,0 +1,127 @@
|
||||
/* resample.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
/************************************************************************************************
|
||||
* *
|
||||
* VERSION FOR COMPLEX DOUBLE-PRECISION *
|
||||
* *
|
||||
************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_resample_h
|
||||
#define wdsp_resample_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API RESAMPLE
|
||||
{
|
||||
public:
|
||||
int run; // run
|
||||
int size; // number of input samples per buffer
|
||||
double* in; // input buffer for resampler
|
||||
double* out; // output buffer for resampler
|
||||
int in_rate;
|
||||
int out_rate;
|
||||
double fcin;
|
||||
double fc;
|
||||
double fc_low;
|
||||
double gain;
|
||||
int idx_in; // index for input into ring
|
||||
int ncoefin;
|
||||
int ncoef; // number of coefficients
|
||||
int L; // interpolation factor
|
||||
int M; // decimation factor
|
||||
double* h; // coefficients
|
||||
int ringsize; // number of complex pairs the ring buffer holds
|
||||
double* ring; // ring buffer
|
||||
int cpp; // coefficients of the phase
|
||||
int phnum; // phase number
|
||||
|
||||
static RESAMPLE* create_resample (int run, int size, double* in, double* out, int in_rate, int out_rate, double fc, int ncoef, double gain);
|
||||
static void destroy_resample (RESAMPLE *a);
|
||||
static void flush_resample (RESAMPLE *a);
|
||||
static int xresample (RESAMPLE *a);
|
||||
static void setBuffers_resample (RESAMPLE *a, double* in, double* out);
|
||||
static void setSize_resample(RESAMPLE *a, int size);
|
||||
static void setInRate_resample(RESAMPLE *a, int rate);
|
||||
static void setOutRate_resample(RESAMPLE *a, int rate);
|
||||
static void setFCLow_resample (RESAMPLE *a, double fc_low);
|
||||
static void setBandwidth_resample (RESAMPLE *a, double fc_low, double fc_high);
|
||||
// Exported calls
|
||||
static void* create_resampleV (int in_rate, int out_rate);
|
||||
static void xresampleV (double* input, double* output, int numsamps, int* outsamps, void* ptr);
|
||||
static void destroy_resampleV (void* ptr);
|
||||
|
||||
private:
|
||||
static void calc_resample (RESAMPLE *a);
|
||||
static void decalc_resample (RESAMPLE *a);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/************************************************************************************************
|
||||
* *
|
||||
* VERSION FOR NON-COMPLEX FLOATS *
|
||||
* *
|
||||
************************************************************************************************/
|
||||
|
||||
#ifndef wdsp_resampleF_h
|
||||
#define wdsp_resampleF_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
class WDSP_API RESAMPLEF
|
||||
{
|
||||
public:
|
||||
int run; // run
|
||||
int size; // number of input samples per buffer
|
||||
float* in; // input buffer for resampler
|
||||
float* out; // output buffer for resampler
|
||||
int idx_in; // index for input into ring
|
||||
int ncoef; // number of coefficients
|
||||
int L; // interpolation factor
|
||||
int M; // decimation factor
|
||||
double* h; // coefficients
|
||||
int ringsize; // number of values the ring buffer holds
|
||||
double* ring; // ring buffer
|
||||
int cpp; // coefficients of the phase
|
||||
int phnum; // phase number
|
||||
|
||||
static RESAMPLEF* create_resampleF (int run, int size, float* in, float* out, int in_rate, int out_rate);
|
||||
static void destroy_resampleF (RESAMPLEF *a);
|
||||
static void flush_resampleF (RESAMPLEF *a);
|
||||
static int xresampleF (RESAMPLEF *a);
|
||||
// Exported calls
|
||||
static void* create_resampleFV (int in_rate, int out_rate);
|
||||
static void xresampleFV (float* input, float* output, int numsamps, int* outsamps, void* ptr);
|
||||
static void destroy_resampleFV (void* ptr);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
745
wdsp/rmatch.cpp
Normal file
745
wdsp/rmatch.cpp
Normal file
@ -0,0 +1,745 @@
|
||||
/* rmatch.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2017, 2018, 2022 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 <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "varsamp.hpp"
|
||||
#include "rmatch.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
MAV* MAV::create_mav (int ringmin, int ringmax, double nom_value)
|
||||
{
|
||||
MAV *a = new MAV;
|
||||
a->ringmin = ringmin;
|
||||
a->ringmax = ringmax;
|
||||
a->nom_value = nom_value;
|
||||
a->ring = new int[a->ringmax]; // (int *) malloc0 (a->ringmax * sizeof (int));
|
||||
a->mask = a->ringmax - 1;
|
||||
a->i = 0;
|
||||
a->load = 0;
|
||||
a->sum = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
void MAV::destroy_mav (MAV *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void MAV::flush_mav (MAV *a)
|
||||
{
|
||||
memset (a->ring, 0, a->ringmax * sizeof (int));
|
||||
a->i = 0;
|
||||
a->load = 0;
|
||||
a->sum = 0;
|
||||
}
|
||||
|
||||
void MAV::xmav (MAV *a, int input, double* output)
|
||||
{
|
||||
if (a->load >= a->ringmax)
|
||||
a->sum -= a->ring[a->i];
|
||||
if (a->load < a->ringmax) a->load++;
|
||||
a->ring[a->i] = input;
|
||||
a->sum += a->ring[a->i];
|
||||
|
||||
if (a->load >= a->ringmin)
|
||||
*output = (double)a->sum / (double)a->load;
|
||||
else
|
||||
*output = a->nom_value;
|
||||
a->i = (a->i + 1) & a->mask;
|
||||
}
|
||||
|
||||
AAMAV* AAMAV::create_aamav (int ringmin, int ringmax, double nom_ratio)
|
||||
{
|
||||
AAMAV *a = new AAMAV;
|
||||
a->ringmin = ringmin;
|
||||
a->ringmax = ringmax;
|
||||
a->nom_ratio = nom_ratio;
|
||||
a->ring = new int[a->ringmax]; // (int *) malloc0 (a->ringmax * sizeof (int));
|
||||
a->mask = a->ringmax - 1;
|
||||
a->i = 0;
|
||||
a->load = 0;
|
||||
a->pos = 0;
|
||||
a->neg = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
void AAMAV::destroy_aamav (AAMAV *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete[] (a);
|
||||
}
|
||||
|
||||
void AAMAV::flush_aamav (AAMAV *a)
|
||||
{
|
||||
memset (a->ring, 0, a->ringmax * sizeof (int));
|
||||
a->i = 0;
|
||||
a->load = 0;
|
||||
a->pos = 0;
|
||||
a->neg = 0;
|
||||
}
|
||||
|
||||
void AAMAV::xaamav (AAMAV *a, int input, double* output)
|
||||
{
|
||||
if (a->load >= a->ringmax)
|
||||
{
|
||||
if (a->ring[a->i] >= 0)
|
||||
a->pos -= a->ring[a->i];
|
||||
else
|
||||
a->neg += a->ring[a->i];
|
||||
}
|
||||
if (a->load <= a->ringmax) a->load++;
|
||||
a->ring[a->i] = input;
|
||||
if (a->ring[a->i] >= 0)
|
||||
a->pos += a->ring[a->i];
|
||||
else
|
||||
a->neg -= a->ring[a->i];
|
||||
if (a->load >= a->ringmin)
|
||||
*output = (double)a->neg / (double)a->pos;
|
||||
else if (a->neg > 0 && a->pos > 0)
|
||||
{
|
||||
double frac = (double)a->load / (double)a->ringmin;
|
||||
*output = (1.0 - frac) * a->nom_ratio + frac * ((double)a->neg / (double)a->pos);
|
||||
}
|
||||
else
|
||||
*output = a->nom_ratio;
|
||||
a->i = (a->i + 1) & a->mask;
|
||||
}
|
||||
|
||||
void RMATCH::calc_rmatch (RMATCH *a)
|
||||
{
|
||||
int m;
|
||||
double theta, dtheta;
|
||||
int max_ring_insize;
|
||||
a->nom_ratio = (double)a->nom_outrate / (double)a->nom_inrate;
|
||||
max_ring_insize = (int)(1.0 + (double)a->insize * (1.05 * a->nom_ratio));
|
||||
if (a->ringsize < 2 * max_ring_insize) a->ringsize = 2 * max_ring_insize;
|
||||
if (a->ringsize < 2 * a->outsize) a->ringsize = 2 * a->outsize;
|
||||
a->ring = new double[a->ringsize * 2]; // (double *) malloc0 (a->ringsize * sizeof (complex));
|
||||
a->rsize = a->ringsize;
|
||||
a->n_ring = a->rsize / 2;
|
||||
a->iin = a->rsize / 2;
|
||||
a->iout = 0;
|
||||
a->resout = new double[max_ring_insize * 2]; // (double *) malloc0 (max_ring_insize * sizeof (complex));
|
||||
a->v = VARSAMP::create_varsamp (1, a->insize, a->in, a->resout, a->nom_inrate, a->nom_outrate,
|
||||
a->fc_high, a->fc_low, a->R, a->gain, a->var, a->varmode);
|
||||
a->ffmav = AAMAV::create_aamav (a->ff_ringmin, a->ff_ringmax, a->nom_ratio);
|
||||
a->propmav = MAV::create_mav (a->prop_ringmin, a->prop_ringmax, 0.0);
|
||||
a->pr_gain = a->prop_gain * 48000.0 / (double)a->nom_outrate; // adjust gain for rate
|
||||
a->inv_nom_ratio = (double)a->nom_inrate / (double)a->nom_outrate;
|
||||
a->feed_forward = 1.0;
|
||||
a->av_deviation = 0.0;
|
||||
a->ntslew = (int)(a->tslew * a->nom_outrate);
|
||||
if (a->ntslew + 1 > a->rsize / 2) a->ntslew = a->rsize / 2 - 1;
|
||||
a->cslew = new double[a->ntslew + 1]; // (double *) malloc0 ((a->ntslew + 1) * sizeof (double));
|
||||
dtheta = PI / (double)a->ntslew;
|
||||
theta = 0.0;
|
||||
for (m = 0; m <= a->ntslew; m++)
|
||||
{
|
||||
a->cslew[m] = 0.5 * (1.0 - cos (theta));
|
||||
theta += dtheta;
|
||||
}
|
||||
a->baux = new double[a->ringsize / 2 * 2]; // (double *) malloc0 (a->ringsize / 2 * sizeof (complex));
|
||||
a->readsamps = 0;
|
||||
a->writesamps = 0;
|
||||
a->read_startup = (unsigned int)((double)a->nom_outrate * a->startup_delay);
|
||||
a->write_startup = (unsigned int)((double)a->nom_inrate * a->startup_delay);
|
||||
a->control_flag = 0;
|
||||
// diagnostics
|
||||
a->underflows = 0;
|
||||
a->overflows = 0;
|
||||
}
|
||||
|
||||
void RMATCH::decalc_rmatch (RMATCH *a)
|
||||
{
|
||||
delete[] (a->baux);
|
||||
delete[] (a->cslew);
|
||||
MAV::destroy_mav (a->propmav);
|
||||
AAMAV::destroy_aamav (a->ffmav);
|
||||
VARSAMP::destroy_varsamp (a->v);
|
||||
delete[] (a->resout);
|
||||
delete[] (a->ring);
|
||||
}
|
||||
|
||||
RMATCH* RMATCH::create_rmatch (
|
||||
int run, // 0 - input and output calls do nothing; 1 - operates normally
|
||||
double* in, // pointer to input buffer
|
||||
double* out, // pointer to output buffer
|
||||
int insize, // size of input buffer
|
||||
int outsize, // size of output buffer
|
||||
int nom_inrate, // nominal input samplerate
|
||||
int nom_outrate, // nominal output samplerate
|
||||
double fc_high, // high cutoff frequency if lower than max
|
||||
double fc_low, // low cutoff frequency if higher than zero
|
||||
double gain, // gain to be applied during this process
|
||||
double startup_delay, // time (seconds) to delay before beginning measurements to control variable resampler
|
||||
int auto_ringsize, // 0 specified ringsize is used; 1 ringsize is auto-optimized - FEATURE NOT IMPLEMENTED!!
|
||||
int ringsize, // specified ringsize; max ringsize if 'auto' is enabled
|
||||
int R, // density factor for varsamp coefficients
|
||||
double var, // initial value of variable resampler ratio (value of ~1.0)
|
||||
int ffmav_min, // minimum feed-forward moving average size to put full weight on data in the ring
|
||||
int ffmav_max, // maximum feed-forward moving average size - MUST BE A POWER OF TWO!
|
||||
double ff_alpha, // feed-forward exponential averaging multiplier
|
||||
int prop_ringmin, // proportional feedback min moving average ringsize
|
||||
int prop_ringmax, // proportional feedback max moving average ringsize - MUST BE A POWER OF TWO!
|
||||
double prop_gain, // proportional feedback gain factor
|
||||
int varmode, // 0 - use same var for all samples of the buffer; 1 - interpolate from old_var to this var
|
||||
double tslew // slew/blend time (seconds)
|
||||
)
|
||||
{
|
||||
RMATCH *a = new RMATCH;
|
||||
a->run = run;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->insize = insize;
|
||||
a->outsize = outsize;
|
||||
a->nom_inrate = nom_inrate;
|
||||
a->nom_outrate = nom_outrate;
|
||||
a->fc_high = fc_high;
|
||||
a->fc_low = fc_low;
|
||||
a->gain = gain;
|
||||
a->startup_delay = startup_delay;
|
||||
a->auto_ringsize = auto_ringsize;
|
||||
a->ringsize = ringsize;
|
||||
a->R = R;
|
||||
a->var = var;
|
||||
a->ff_ringmin = ffmav_min;
|
||||
a->ff_ringmax = ffmav_max; // must be a power of two
|
||||
a->ff_alpha = ff_alpha;
|
||||
a->prop_ringmin = prop_ringmin;
|
||||
a->prop_ringmax = prop_ringmax; // must be a power of two
|
||||
a->prop_gain = prop_gain;
|
||||
a->varmode = varmode;
|
||||
a->tslew = tslew;
|
||||
calc_rmatch(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void RMATCH::destroy_rmatch (RMATCH *a)
|
||||
{
|
||||
decalc_rmatch (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void RMATCH::reset_rmatch (RMATCH *a)
|
||||
{
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
decalc_rmatch(a);
|
||||
calc_rmatch (a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
void RMATCH::control (RMATCH *a, int change)
|
||||
{
|
||||
{
|
||||
double current_ratio;
|
||||
AAMAV::xaamav (a->ffmav, change, ¤t_ratio);
|
||||
current_ratio *= a->inv_nom_ratio;
|
||||
a->feed_forward = a->ff_alpha * current_ratio + (1.0 - a->ff_alpha) * a->feed_forward;
|
||||
}
|
||||
{
|
||||
int deviation = a->n_ring - a->rsize / 2;
|
||||
MAV::xmav (a->propmav, deviation, &a->av_deviation);
|
||||
}
|
||||
a->cs_var.lock();
|
||||
a->var = a->feed_forward - a->pr_gain * a->av_deviation;
|
||||
if (a->var > 1.04) a->var = 1.04;
|
||||
if (a->var < 0.96) a->var = 0.96;
|
||||
a->cs_var.unlock();
|
||||
}
|
||||
|
||||
void RMATCH::blend (RMATCH *a)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0, j = a->iout; i <= a->ntslew; i++, j = (j + 1) % a->rsize)
|
||||
{
|
||||
a->ring[2 * j + 0] = a->cslew[i] * a->ring[2 * j + 0] + (1.0 - a->cslew[i]) * a->baux[2 * i + 0];
|
||||
a->ring[2 * j + 1] = a->cslew[i] * a->ring[2 * j + 1] + (1.0 - a->cslew[i]) * a->baux[2 * i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
void RMATCH::upslew (RMATCH *a, int newsamps)
|
||||
{
|
||||
int i, j;
|
||||
i = 0;
|
||||
j = a->iin;
|
||||
while (a->ucnt >= 0 && i < newsamps)
|
||||
{
|
||||
a->ring[2 * j + 0] *= a->cslew[a->ntslew - a->ucnt];
|
||||
a->ring[2 * j + 1] *= a->cslew[a->ntslew - a->ucnt];
|
||||
a->ucnt--;
|
||||
i++;
|
||||
j = (j + 1) % a->rsize;
|
||||
}
|
||||
}
|
||||
|
||||
void RMATCH::xrmatchIN (void* b, double* in)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
if (a->run == 1)
|
||||
{
|
||||
int newsamps, first, second, ovfl;
|
||||
double var;
|
||||
a->v->in = a->in = in;
|
||||
a->cs_var.lock();
|
||||
if (!a->force)
|
||||
var = a->var;
|
||||
else
|
||||
var = a->fvar;
|
||||
a->cs_var.unlock();
|
||||
newsamps = VARSAMP::xvarsamp (a->v, var);
|
||||
a->cs_ring.lock();
|
||||
a->n_ring += newsamps;
|
||||
if ((ovfl = a->n_ring - a->rsize) > 0)
|
||||
{
|
||||
a->overflows += 1;
|
||||
// a->n_ring = a->rsize / 2;
|
||||
a->n_ring = a->rsize; //
|
||||
if ((a->ntslew + 1) > (a->rsize - a->iout))
|
||||
{
|
||||
first = a->rsize - a->iout;
|
||||
second = (a->ntslew + 1) - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = a->ntslew + 1;
|
||||
second = 0;
|
||||
}
|
||||
memcpy (a->baux, a->ring + 2 * a->iout, first * sizeof (dcomplex));
|
||||
memcpy (a->baux + 2 * first, a->ring, second * sizeof (dcomplex));
|
||||
// a->iout = (a->iout + ovfl + a->rsize / 2) % a->rsize;
|
||||
a->iout = (a->iout + ovfl) % a->rsize; //
|
||||
}
|
||||
if (newsamps > (a->rsize - a->iin))
|
||||
{
|
||||
first = a->rsize - a->iin;
|
||||
second = newsamps - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = newsamps;
|
||||
second = 0;
|
||||
}
|
||||
memcpy (a->ring + 2 * a->iin, a->resout, first * sizeof (dcomplex));
|
||||
memcpy (a->ring, a->resout + 2 * first, second * sizeof (dcomplex));
|
||||
if (a->ucnt >= 0) upslew(a, newsamps);
|
||||
a->iin = (a->iin + newsamps) % a->rsize;
|
||||
if (ovfl > 0) blend (a);
|
||||
if (!a->control_flag)
|
||||
{
|
||||
a->writesamps += a->insize;
|
||||
if ((a->readsamps >= a->read_startup) && (a->writesamps >= a->write_startup))
|
||||
a->control_flag = 1;
|
||||
}
|
||||
if (a->control_flag) control (a, a->insize);
|
||||
a->cs_ring.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void RMATCH::dslew (RMATCH *a)
|
||||
{
|
||||
int i, j, k, n;
|
||||
int zeros, first, second;
|
||||
if (a->n_ring > a->ntslew + 1)
|
||||
{
|
||||
i = (a->iout + (a->n_ring - (a->ntslew + 1))) % a->rsize;
|
||||
j = a->ntslew;
|
||||
k = a->ntslew + 1;
|
||||
n = a->n_ring - (a->ntslew + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
i = a->iout;
|
||||
j = a->ntslew;
|
||||
k = a->n_ring;
|
||||
n = 0;
|
||||
}
|
||||
while (k > 0 && j >= 0)
|
||||
{
|
||||
if (k == 1)
|
||||
{
|
||||
a->dlast[0] = a->ring[2 * i + 0];
|
||||
a->dlast[1] = a->ring[2 * i + 1];
|
||||
}
|
||||
a->ring[2 * i + 0] *= a->cslew[j];
|
||||
a->ring[2 * i + 1] *= a->cslew[j];
|
||||
i = (i + 1) % a->rsize;
|
||||
j--;
|
||||
k--;
|
||||
n++;
|
||||
}
|
||||
while (j >= 0)
|
||||
{
|
||||
a->ring[2 * i + 0] = a->dlast[0] * a->cslew[j];
|
||||
a->ring[2 * i + 1] = a->dlast[1] * a->cslew[j];
|
||||
i = (i + 1) % a->rsize;
|
||||
j--;
|
||||
n++;
|
||||
}
|
||||
// zeros = a->outsize + a->rsize / 2 - n;
|
||||
if ((zeros = a->outsize - n) > 0) //
|
||||
{ //
|
||||
if (zeros > a->rsize - i)
|
||||
{
|
||||
first = a->rsize - i;
|
||||
second = zeros - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = zeros;
|
||||
second = 0;
|
||||
}
|
||||
memset (a->ring + 2 * i, 0, first * sizeof (dcomplex));
|
||||
memset (a->ring, 0, second * sizeof (dcomplex));
|
||||
n += zeros; //
|
||||
} //
|
||||
// a->n_ring = a->outsize + a->rsize / 2;
|
||||
a->n_ring = n; //
|
||||
// a->iin = (a->iout + a->outsize + a->rsize/2) % a->rsize;
|
||||
a->iin = (a->iout + a->n_ring) % a->rsize; //
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::xrmatchOUT (void* b, double* out)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
if (a->run == 1)
|
||||
{
|
||||
int first, second;
|
||||
a->out = out;
|
||||
a->cs_ring.lock();
|
||||
if (a->n_ring < a->outsize)
|
||||
{
|
||||
dslew (a);
|
||||
a->ucnt = a->ntslew;
|
||||
a->underflows += 1;
|
||||
}
|
||||
if (a->outsize > (a->rsize - a->iout))
|
||||
{
|
||||
first = a->rsize - a->iout;
|
||||
second = a->outsize - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = a->outsize;
|
||||
second = 0;
|
||||
}
|
||||
memcpy (a->out, a->ring + 2 * a->iout, first * sizeof (dcomplex));
|
||||
memcpy (a->out + 2 * first, a->ring, second * sizeof (dcomplex));
|
||||
a->iout = (a->iout + a->outsize) % a->rsize;
|
||||
a->n_ring -= a->outsize;
|
||||
a->dlast[0] = a->out[2 * (a->outsize - 1) + 0];
|
||||
a->dlast[1] = a->out[2 * (a->outsize - 1) + 1];
|
||||
if (!a->control_flag)
|
||||
{
|
||||
a->readsamps += a->outsize;
|
||||
if ((a->readsamps >= a->read_startup) && (a->writesamps >= a->write_startup))
|
||||
a->control_flag = 1;
|
||||
}
|
||||
if (a->control_flag) control (a, -(a->outsize));
|
||||
a->cs_ring.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::getRMatchDiags (void* b, int* underflows, int* overflows, double* var, int* ringsize, int* nring)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
*underflows = a->underflows;
|
||||
*overflows = a->overflows;
|
||||
a->underflows &= 0xFFFFFFFF;
|
||||
a->overflows &= 0xFFFFFFFF;
|
||||
a->cs_var.lock();
|
||||
*var = a->var;
|
||||
*ringsize = a->ringsize;
|
||||
*nring = a->n_ring;
|
||||
a->cs_var.unlock();
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::resetRMatchDiags (void*)
|
||||
{
|
||||
// RMATCH *a = (RMATCH*) b;
|
||||
// InterlockedExchange (&a->underflows, 0);
|
||||
// InterlockedExchange (&a->overflows, 0);
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::forceRMatchVar (void* b, int force, double fvar)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
a->cs_var.lock();
|
||||
a->force = force;
|
||||
a->fvar = fvar;
|
||||
a->cs_var.unlock();
|
||||
}
|
||||
|
||||
|
||||
void* RMATCH::create_rmatchV(int in_size, int out_size, int nom_inrate, int nom_outrate, int ringsize, double var)
|
||||
{
|
||||
return (void*)create_rmatch (
|
||||
1, // run
|
||||
0, // input buffer, stuffed in other calls
|
||||
0, // output buffer, stuffed in other calls
|
||||
in_size, // input buffer size (complex samples)
|
||||
out_size, // output buffer size (complex samples)
|
||||
nom_inrate, // nominal input sample-rate
|
||||
nom_outrate, // nominal output sample-rate
|
||||
0.0, // fc_high (0.0 -> automatic)
|
||||
-1.0, // fc_low (-1.0 -> no low cutoff)
|
||||
1.0, // gain
|
||||
3.0, // startup delay (seconds)
|
||||
1, // automatic ring-size [not implemented yet]
|
||||
ringsize, // ringsize
|
||||
1024, // R, coefficient density
|
||||
var, // initial variable ratio
|
||||
4096, // feed-forward moving average min size
|
||||
262144, // feed-forward moving average max size - POWER OF TWO!
|
||||
0.01, // feed-forward exponential smoothing
|
||||
4096, // proportional feedback min moving av ringsize
|
||||
16384, // proportional feedback max moving av ringsize - POWER OF TWO!
|
||||
4.0e-06, // proportional feedback gain
|
||||
1, // linearly interpolate cvar by sample
|
||||
0.003 ); // slew time (seconds)
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::destroy_rmatchV (void* ptr)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
destroy_rmatch (a);
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchInsize (void* ptr, int insize)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
decalc_rmatch(a);
|
||||
a->insize = insize;
|
||||
calc_rmatch (a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchOutsize (void* ptr, int outsize)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
decalc_rmatch(a);
|
||||
a->outsize = outsize;
|
||||
calc_rmatch (a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchNomInrate (void* ptr, int nom_inrate)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
decalc_rmatch(a);
|
||||
a->nom_inrate = nom_inrate;
|
||||
calc_rmatch (a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchNomOutrate (void* ptr, int nom_outrate)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
decalc_rmatch(a);
|
||||
a->nom_outrate = nom_outrate;
|
||||
calc_rmatch (a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchRingsize (void* ptr, int ringsize)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
decalc_rmatch(a);
|
||||
a->ringsize = ringsize;
|
||||
calc_rmatch (a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchFeedbackGain (void* b, double feedback_gain)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
a->cs_var.lock();
|
||||
a->prop_gain = feedback_gain;
|
||||
a->pr_gain = a->prop_gain * 48000.0 / (double)a->nom_outrate;
|
||||
a->cs_var.unlock();
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchSlewTime (void* b, double slew_time)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
a->run = 0; // InterlockedBitTestAndReset(&a->run, 0); // turn OFF new data coming into the rmatch
|
||||
// Sleep(10); // wait for processing to cease
|
||||
decalc_rmatch(a); // deallocate all memory EXCEPT the data structure holding all current parameters
|
||||
a->tslew = slew_time; // change the value of 'slew_time'
|
||||
calc_rmatch(a); // recalculate/reallocate everything in the RMATCH
|
||||
a->run = 1; // InterlockedBitTestAndSet(&a->run, 0); // turn ON the dataflow
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchSlewTime1(void* b, double slew_time)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) b;
|
||||
double theta, dtheta;
|
||||
int m;
|
||||
a->run = 0;
|
||||
// Sleep(10);
|
||||
delete[](a->cslew);
|
||||
a->tslew = slew_time;
|
||||
a->ntslew = (int)(a->tslew * a->nom_outrate);
|
||||
if (a->ntslew + 1 > a->rsize / 2) a->ntslew = a->rsize / 2 - 1;
|
||||
a->cslew = new double[a->ntslew + 1]; // (double*)malloc0((a->ntslew + 1) * sizeof(double));
|
||||
dtheta = PI / (double)a->ntslew;
|
||||
theta = 0.0;
|
||||
for (m = 0; m <= a->ntslew; m++)
|
||||
{
|
||||
a->cslew[m] = 0.5 * (1.0 - cos(theta));
|
||||
theta += dtheta;
|
||||
}
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchPropRingMin(void* ptr, int prop_min)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
// Sleep(10);
|
||||
decalc_rmatch(a);
|
||||
a->prop_ringmin = prop_min;
|
||||
calc_rmatch(a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchPropRingMax(void* ptr, int prop_max)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
// Sleep(10);
|
||||
decalc_rmatch(a);
|
||||
a->prop_ringmax = prop_max; // must be a power of two
|
||||
calc_rmatch(a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchFFRingMin(void* ptr, int ff_ringmin)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
// Sleep(10);
|
||||
decalc_rmatch(a);
|
||||
a->ff_ringmin = ff_ringmin;
|
||||
calc_rmatch(a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchFFRingMax(void* ptr, int ff_ringmax)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
// Sleep(10);
|
||||
decalc_rmatch(a);
|
||||
a->ff_ringmax = ff_ringmax; // must be a power of two
|
||||
calc_rmatch(a);
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::setRMatchFFAlpha(void* ptr, double ff_alpha)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->run = 0;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
a->ff_alpha = ff_alpha;
|
||||
a->run = 1;
|
||||
}
|
||||
|
||||
|
||||
void RMATCH::getControlFlag(void* ptr, int* control_flag)
|
||||
{
|
||||
RMATCH *a = (RMATCH*) ptr;
|
||||
a->cs_ring.lock();
|
||||
*control_flag = a->control_flag;
|
||||
a->cs_ring.unlock();
|
||||
}
|
||||
|
||||
// the following function is DEPRECATED
|
||||
// it is intended for Legacy PowerSDR use only
|
||||
|
||||
void* RMATCH::create_rmatchLegacyV(int in_size, int out_size, int nom_inrate, int nom_outrate, int ringsize)
|
||||
{
|
||||
return (void*) create_rmatch(
|
||||
1, // run
|
||||
0, // input buffer, stuffed in other calls
|
||||
0, // output buffer, stuffed in other calls
|
||||
in_size, // input buffer size (complex samples)
|
||||
out_size, // output buffer size (complex samples)
|
||||
nom_inrate, // nominal input sample-rate
|
||||
nom_outrate, // nominal output sample-rate
|
||||
0.0, // fc_high (0.0 -> automatic)
|
||||
-1.0, // fc_low (-1.0 -> no low cutoff)
|
||||
1.0, // gain
|
||||
3.0, // startup delay (seconds)
|
||||
1, // automatic ring-size [not implemented yet]
|
||||
ringsize, // ringsize
|
||||
1024, // R, coefficient density
|
||||
1.0, // initial variable ratio
|
||||
4096, // feed-forward moving average min size
|
||||
262144, // feed-forward moving average max size - POWER OF TWO!
|
||||
0.01, // feed-forward exponential smoothing
|
||||
4096, // proportional feedback min moving av ringsize
|
||||
16384, // proportional feedback max moving av ringsize - POWER OF TWO!
|
||||
1.0e-06, // proportional feedback gain ***W4WMT - reduce loop gain a bit for PowerSDR to help Primary buffers > 512
|
||||
0, // linearly interpolate cvar by sample ***W4WMT - set varmode = 0 for PowerSDR (doesn't work otherwise!?!)
|
||||
0.003); // slew time (seconds)
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
199
wdsp/rmatch.hpp
Normal file
199
wdsp/rmatch.hpp
Normal file
@ -0,0 +1,199 @@
|
||||
/* rmatch.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2017, 2022 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_rmatch_h
|
||||
#define wdsp_rmatch_h
|
||||
|
||||
#include <atomic>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class VARSAMP;
|
||||
|
||||
class WDSP_API MAV
|
||||
{
|
||||
public:
|
||||
int ringmin;
|
||||
int ringmax; // must be a power of two
|
||||
int* ring;
|
||||
int mask;
|
||||
int i;
|
||||
int load;
|
||||
int sum;
|
||||
double nom_value;
|
||||
|
||||
static MAV* create_mav (int ringmin, int ringmax, double nom_value);
|
||||
static void destroy_mav (MAV *a);
|
||||
static void flush_mav (MAV *a);
|
||||
static void xmav (MAV *a, int input, double* output);
|
||||
};
|
||||
|
||||
class WDSP_API AAMAV
|
||||
{
|
||||
public:
|
||||
int ringmin;
|
||||
int ringmax; // must be a power of two
|
||||
int* ring;
|
||||
int mask;
|
||||
int i;
|
||||
int load;
|
||||
int pos;
|
||||
int neg;
|
||||
double nom_ratio;
|
||||
|
||||
static AAMAV* create_aamav (int ringmin, int ringmax, double nom_ratio);
|
||||
static void destroy_aamav (AAMAV *a);
|
||||
static void flush_aamav (AAMAV *a);
|
||||
static void xaamav (AAMAV *a, int input, double* output);
|
||||
};
|
||||
|
||||
class WDSP_API RMATCH
|
||||
{
|
||||
public:
|
||||
std::atomic<long> run;
|
||||
double* in;
|
||||
double* out;
|
||||
int insize;
|
||||
int outsize;
|
||||
double* resout;
|
||||
int nom_inrate;
|
||||
int nom_outrate;
|
||||
double nom_ratio;
|
||||
double inv_nom_ratio;
|
||||
double fc_high;
|
||||
double fc_low;
|
||||
double gain;
|
||||
double startup_delay;
|
||||
int auto_ringsize;
|
||||
int ringsize;
|
||||
int rsize;
|
||||
double* ring;
|
||||
int n_ring;
|
||||
int iin;
|
||||
int iout;
|
||||
double var;
|
||||
int R;
|
||||
AAMAV *ffmav;
|
||||
MAV *propmav;
|
||||
int ff_ringmin;
|
||||
int ff_ringmax; // must be a power of two
|
||||
double ff_alpha;
|
||||
double feed_forward;
|
||||
int prop_ringmin;
|
||||
int prop_ringmax; // must be a power of two
|
||||
double prop_gain;
|
||||
double pr_gain;
|
||||
double av_deviation;
|
||||
VARSAMP *v;
|
||||
int varmode;
|
||||
QRecursiveMutex cs_ring;
|
||||
QRecursiveMutex cs_var;
|
||||
// blend / slew
|
||||
double tslew;
|
||||
int ntslew;
|
||||
double* cslew;
|
||||
double* baux;
|
||||
double dlast[2];
|
||||
int ucnt;
|
||||
// variables to check start-up time for control to become active
|
||||
unsigned int readsamps;
|
||||
unsigned int writesamps;
|
||||
unsigned int read_startup;
|
||||
unsigned int write_startup;
|
||||
int control_flag;
|
||||
// diagnostics
|
||||
std::atomic<long> underflows;
|
||||
std::atomic<long> overflows;
|
||||
int force;
|
||||
double fvar;
|
||||
|
||||
static RMATCH* create_rmatch (
|
||||
int run, // 0 - input and output calls do nothing; 1 - operates normally
|
||||
double* in, // pointer to input buffer
|
||||
double* out, // pointer to output buffer
|
||||
int insize, // size of input buffer
|
||||
int outsize, // size of output buffer
|
||||
int nom_inrate, // nominal input samplerate
|
||||
int nom_outrate, // nominal output samplerate
|
||||
double fc_high, // high cutoff frequency if lower than max
|
||||
double fc_low, // low cutoff frequency if higher than zero
|
||||
double gain, // gain to be applied during this process
|
||||
double startup_delay, // time (seconds) to delay before beginning measurements to control variable resampler
|
||||
int auto_ringsize, // 0 specified ringsize is used; 1 ringsize is auto-optimized - FEATURE NOT IMPLEMENTED!!
|
||||
int ringsize, // specified ringsize; max ringsize if 'auto' is enabled
|
||||
int R, // density factor for varsamp coefficients
|
||||
double var, // initial value of variable resampler ratio (value of ~1.0)
|
||||
int ffmav_min, // minimum feed-forward moving average size to put full weight on data in the ring
|
||||
int ffmav_max, // maximum feed-forward moving average size - MUST BE A POWER OF TWO!
|
||||
double ff_alpha, // feed-forward exponential averaging multiplier
|
||||
int prop_ringmin, // proportional feedback min moving average ringsize
|
||||
int prop_ringmax, // proportional feedback max moving average ringsize - MUST BE A POWER OF TWO!
|
||||
double prop_gain, // proportional feedback gain factor
|
||||
int varmode, // 0 - use same var for all samples of the buffer; 1 - interpolate from old_var to this var
|
||||
double tslew // slew/blend time (seconds)
|
||||
);
|
||||
static void destroy_rmatch (RMATCH *a);
|
||||
static void reset_rmatch (RMATCH *a);
|
||||
|
||||
static void* create_rmatchV(int in_size, int out_size, int nom_inrate, int nom_outrate, int ringsize, double var);
|
||||
static void* create_rmatchLegacyV(int in_size, int out_size, int nom_inrate, int nom_outrate, int ringsize);
|
||||
static void destroy_rmatchV (void* ptr);
|
||||
static void xrmatchOUT (void* b, double* out);
|
||||
static void xrmatchIN (void* b, double* in);
|
||||
static void setRMatchInsize (void* ptr, int insize);
|
||||
static void setRMatchOutsize (void* ptr, int outsize);
|
||||
static void setRMatchNomInrate (void* ptr, int nom_inrate);
|
||||
static void setRMatchNomOutrate (void* ptr, int nom_outrate);
|
||||
static void setRMatchRingsize (void* ptr, int ringsize);
|
||||
static void getRMatchDiags (void* b, int* underflows, int* overflows, double* var, int* ringsize, int* nring);
|
||||
static void resetRMatchDiags (void* b);
|
||||
static void forceRMatchVar (void* b, int force, double fvar);
|
||||
static void setRMatchFeedbackGain(void* b, double feedback_gain);
|
||||
static void setRMatchSlewTime(void* b, double slew_time);
|
||||
static void setRMatchSlewTime1(void* b, double slew_time);
|
||||
static void setRMatchPropRingMin(void* ptr, int prop_min);
|
||||
static void setRMatchPropRingMax(void* ptr, int prop_max);
|
||||
static void setRMatchFFRingMin(void* ptr, int ff_ringmin);
|
||||
static void setRMatchFFRingMax(void* ptr, int ff_ringmax);
|
||||
static void setRMatchFFAlpha(void* ptr, double ff_alpha);
|
||||
static void getControlFlag(void* ptr, int* control_flag);
|
||||
|
||||
private:
|
||||
static void calc_rmatch (RMATCH *a);
|
||||
static void decalc_rmatch (RMATCH *a);
|
||||
static void control (RMATCH *a, int change);
|
||||
static void blend (RMATCH *a);
|
||||
static void upslew (RMATCH *a, int newsamps);
|
||||
static void dslew (RMATCH *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
128
wdsp/sender.cpp
Normal file
128
wdsp/sender.cpp
Normal file
@ -0,0 +1,128 @@
|
||||
/* sender.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "sender.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void SENDER::calc_sender (SENDER *a)
|
||||
{
|
||||
a->out = new double[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void decalc_sender (SENDER *a)
|
||||
{
|
||||
delete[] (a->out);
|
||||
}
|
||||
|
||||
SENDER* SENDER::create_sender (int run, int flag, int mode, int size, double* in, int arg0, int arg1, int arg2, int arg3)
|
||||
{
|
||||
SENDER *a = new SENDER;
|
||||
a->run = run;
|
||||
a->flag = flag;
|
||||
a->mode = mode;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->arg0 = arg0;
|
||||
a->arg1 = arg1;
|
||||
a->arg2 = arg2;
|
||||
a->arg3 = arg3;
|
||||
calc_sender (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void SENDER::destroy_sender (SENDER *a)
|
||||
{
|
||||
decalc_sender (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void SENDER::flush_sender (SENDER *a)
|
||||
{
|
||||
memset (a->out, 0, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void SENDER::xsender (SENDER *a)
|
||||
{
|
||||
if (a->run && a->flag)
|
||||
{
|
||||
switch (a->mode)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
int i;
|
||||
dINREAL* outf = (dINREAL *)a->out;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
outf [2 * i + 0] = (dINREAL)a->in[2 * i + 0];
|
||||
outf [2 * i + 1] = (dINREAL)a->in[2 * i + 1];
|
||||
}
|
||||
// Spectrum2 (1, a->arg0, a->arg1, a->arg2, outf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SENDER::setBuffers_sender (SENDER *a, double* in)
|
||||
{
|
||||
a->in = in;
|
||||
}
|
||||
|
||||
void SENDER::setSamplerate_sender (SENDER *a, int)
|
||||
{
|
||||
flush_sender (a);
|
||||
}
|
||||
|
||||
void SENDER::setSize_sender (SENDER *a, int size)
|
||||
{
|
||||
decalc_sender (a);
|
||||
a->size = size;
|
||||
calc_sender (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SENDER::SetSpectrum (RXA& rxa, int flag, int disp, int ss, int LO)
|
||||
{
|
||||
SENDER *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.sender.p;
|
||||
a->flag = flag;
|
||||
a->arg0 = disp;
|
||||
a->arg1 = ss;
|
||||
a->arg2 = LO;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
75
wdsp/sender.hpp
Normal file
75
wdsp/sender.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
/* send.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
// 'send' copies samples from the sample stream and proactively pushes those samples to external
|
||||
// functions by calling them. As such, the call and parameters are somewhat specific to the
|
||||
// need for the data. Nevertheless, this code is written in somewhat of a generic form to
|
||||
// facilitate adding additional functions in the future.
|
||||
|
||||
#ifndef wdsp_sender_h
|
||||
#define wdsp_sender_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API SENDER
|
||||
{
|
||||
public:
|
||||
int run; // 0 = send OFF, 1 = send ON
|
||||
int flag; // secondary 'run'; AND'd with 'run'
|
||||
int mode; // selects the specific processing and function call
|
||||
int size; // size of the data buffer (complex samples)
|
||||
double* in; // buffer from which to take the data
|
||||
int arg0; // parameters that can be passed to the function called
|
||||
int arg1;
|
||||
int arg2;
|
||||
int arg3;
|
||||
double* out; // internally created buffer into which processed data is placed
|
||||
// a pointer to *out is passed to the external function that is called
|
||||
|
||||
static SENDER* create_sender (int run, int flag, int mode, int size, double* in, int arg0, int arg1, int arg2, int arg3);
|
||||
static void destroy_sender (SENDER *a);
|
||||
static void flush_sender (SENDER *a);
|
||||
static void xsender (SENDER *a);
|
||||
static void setBuffers_sender (SENDER *a, double* in);
|
||||
static void setSamplerate_sender (SENDER *a, int rate);
|
||||
static void setSize_sender (SENDER *a, int size);
|
||||
// RXA Properties
|
||||
static void SetSpectrum (RXA& rxa, int flag, int disp, int ss, int LO);
|
||||
// TXA Properties
|
||||
|
||||
private:
|
||||
static void calc_sender (SENDER *a);
|
||||
static void decalc_sender (SENDER *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
132
wdsp/shift.cpp
Normal file
132
wdsp/shift.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/* shift.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "shift.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void SHIFT::calc_shift (SHIFT *a)
|
||||
{
|
||||
a->delta = TWOPI * a->shift / a->rate;
|
||||
a->cos_delta = cos (a->delta);
|
||||
a->sin_delta = sin (a->delta);
|
||||
}
|
||||
|
||||
SHIFT* SHIFT::create_shift (int run, int size, double* in, double* out, int rate, double fshift)
|
||||
{
|
||||
SHIFT *a = new SHIFT;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = (double)rate;
|
||||
a->shift = fshift;
|
||||
a->phase = 0.0;
|
||||
calc_shift (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void SHIFT::destroy_shift (SHIFT *a)
|
||||
{
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void SHIFT::flush_shift (SHIFT *a)
|
||||
{
|
||||
a->phase = 0.0;
|
||||
}
|
||||
|
||||
void SHIFT::xshift (SHIFT *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double I1, Q1, t1, t2;
|
||||
double cos_phase = cos (a->phase);
|
||||
double sin_phase = sin (a->phase);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
I1 = a->in[2 * i + 0];
|
||||
Q1 = a->in[2 * i + 1];
|
||||
a->out[2 * i + 0] = I1 * cos_phase - Q1 * sin_phase;
|
||||
a->out[2 * i + 1] = I1 * sin_phase + Q1 * cos_phase;
|
||||
t1 = cos_phase;
|
||||
t2 = sin_phase;
|
||||
cos_phase = t1 * a->cos_delta - t2 * a->sin_delta;
|
||||
sin_phase = t1 * a->sin_delta + t2 * a->cos_delta;
|
||||
a->phase += a->delta;
|
||||
if (a->phase >= TWOPI) a->phase -= TWOPI;
|
||||
if (a->phase < 0.0 ) a->phase += TWOPI;
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void SHIFT::setBuffers_shift(SHIFT *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void SHIFT::setSamplerate_shift (SHIFT *a, int rate)
|
||||
{
|
||||
a->rate = rate;
|
||||
a->phase = 0.0;
|
||||
calc_shift(a);
|
||||
}
|
||||
|
||||
void SHIFT::setSize_shift (SHIFT *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_shift (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SHIFT::SetShiftRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.shift.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SHIFT::SetShiftFreq (RXA& rxa, double fshift)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.shift.p->shift = fshift;
|
||||
calc_shift (rxa.shift.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
68
wdsp/shift.hpp
Normal file
68
wdsp/shift.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
/* shift.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_shift_h
|
||||
#define wdsp_shift_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
|
||||
class WDSP_API SHIFT
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double shift;
|
||||
double phase;
|
||||
double delta;
|
||||
double cos_delta;
|
||||
double sin_delta;
|
||||
|
||||
static SHIFT* create_shift (int run, int size, double* in, double* out, int rate, double fshift);
|
||||
static void destroy_shift (SHIFT *a);
|
||||
static void flush_shift (SHIFT *a);
|
||||
static void xshift (SHIFT *a);
|
||||
static void setBuffers_shift (SHIFT *a, double* in, double* out);
|
||||
static void setSamplerate_shift (SHIFT *a, int rate);
|
||||
static void setSize_shift (SHIFT *a, int size);
|
||||
// RXA Properties
|
||||
static void SetShiftRun (RXA& rxa, int run);
|
||||
static void SetShiftFreq (RXA& rxa, double fshift);
|
||||
|
||||
private:
|
||||
static void calc_shift (SHIFT *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
383
wdsp/siphon.cpp
Normal file
383
wdsp/siphon.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
/* siphon.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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 "meterlog10.hpp"
|
||||
#include "siphon.hpp"
|
||||
#include "RXA.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void SIPHON::build_window (SIPHON *a)
|
||||
{
|
||||
int i;
|
||||
double arg0, cosphi;
|
||||
double sum, scale;
|
||||
arg0 = 2.0 * PI / ((double)a->fftsize - 1.0);
|
||||
sum = 0.0;
|
||||
for (i = 0; i < a->fftsize; i++)
|
||||
{
|
||||
cosphi = cos (arg0 * (double)i);
|
||||
a->window[i] = + 6.3964424114390378e-02
|
||||
+ cosphi * ( - 2.3993864599352804e-01
|
||||
+ cosphi * ( + 3.5015956323820469e-01
|
||||
+ cosphi * ( - 2.4774111897080783e-01
|
||||
+ cosphi * ( + 8.5438256055858031e-02
|
||||
+ cosphi * ( - 1.2320203369293225e-02
|
||||
+ cosphi * ( + 4.3778825791773474e-04 ))))));
|
||||
sum += a->window[i];
|
||||
}
|
||||
scale = 1.0 / sum;
|
||||
for (i = 0; i < a->fftsize; i++)
|
||||
a->window[i] *= scale;
|
||||
}
|
||||
|
||||
SIPHON* SIPHON::create_siphon (
|
||||
int run,
|
||||
int position,
|
||||
int mode,
|
||||
int disp,
|
||||
int insize,
|
||||
double* in,
|
||||
int sipsize,
|
||||
int fftsize,
|
||||
int specmode
|
||||
)
|
||||
{
|
||||
SIPHON *a = new SIPHON;
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->mode = mode;
|
||||
a->disp = disp;
|
||||
a->insize = insize;
|
||||
a->in = in;
|
||||
a->sipsize = sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!!
|
||||
a->fftsize = fftsize;
|
||||
a->specmode = specmode;
|
||||
a->sipbuff = new double[a->sipsize * 2]; // (double *) malloc0 (a->sipsize * sizeof (complex));
|
||||
a->idx = 0;
|
||||
a->sipout = new double[a->sipsize * 2]; // (double *) malloc0 (a->sipsize * sizeof (complex));
|
||||
a->specout = new double[a->fftsize * 2]; // (double *) malloc0 (a->fftsize * sizeof (complex));
|
||||
a->sipplan = fftw_plan_dft_1d (a->fftsize, (fftw_complex *)a->sipout, (fftw_complex *)a->specout, FFTW_FORWARD, FFTW_PATIENT);
|
||||
a->window = new double[a->fftsize * 2]; // (double *) malloc0 (a->fftsize * sizeof (complex));
|
||||
build_window (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void SIPHON::destroy_siphon (SIPHON *a)
|
||||
{
|
||||
fftw_destroy_plan (a->sipplan);
|
||||
delete[] (a->window);
|
||||
delete[] (a->specout);
|
||||
delete[] (a->sipout);
|
||||
delete[] (a->sipbuff);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void SIPHON::flush_siphon (SIPHON *a)
|
||||
{
|
||||
memset (a->sipbuff, 0, a->sipsize * sizeof (dcomplex));
|
||||
memset (a->sipout , 0, a->sipsize * sizeof (dcomplex));
|
||||
memset (a->specout, 0, a->fftsize * sizeof (dcomplex));
|
||||
a->idx = 0;
|
||||
}
|
||||
|
||||
void SIPHON::xsiphon (SIPHON *a, int pos)
|
||||
{
|
||||
int first, second;
|
||||
a->update.lock();
|
||||
if (a->run && a->position == pos)
|
||||
{
|
||||
switch (a->mode)
|
||||
{
|
||||
case 0:
|
||||
if (a->insize >= a->sipsize)
|
||||
memcpy (a->sipbuff, &(a->in[2 * (a->insize - a->sipsize)]), a->sipsize * sizeof (dcomplex));
|
||||
else
|
||||
{
|
||||
if (a->insize > (a->sipsize - a->idx))
|
||||
{
|
||||
first = a->sipsize - a->idx;
|
||||
second = a->insize - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = a->insize;
|
||||
second = 0;
|
||||
}
|
||||
memcpy (a->sipbuff + 2 * a->idx, a->in, first * sizeof (dcomplex));
|
||||
memcpy (a->sipbuff, a->in + 2 * first, second * sizeof (dcomplex));
|
||||
if ((a->idx += a->insize) >= a->sipsize) a->idx -= a->sipsize;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// Spectrum0 (1, a->disp, 0, 0, a->in);
|
||||
break;
|
||||
}
|
||||
}
|
||||
a->update.unlock();
|
||||
}
|
||||
|
||||
void SIPHON::setBuffers_siphon (SIPHON *a, double* in)
|
||||
{
|
||||
a->in = in;
|
||||
}
|
||||
|
||||
void SIPHON::setSamplerate_siphon (SIPHON *a, int)
|
||||
{
|
||||
flush_siphon (a);
|
||||
}
|
||||
|
||||
void SIPHON::setSize_siphon (SIPHON *a, int size)
|
||||
{
|
||||
a->insize = size;
|
||||
flush_siphon (a);
|
||||
}
|
||||
|
||||
void SIPHON::suck (SIPHON *a)
|
||||
{
|
||||
if (a->outsize <= a->sipsize)
|
||||
{
|
||||
int mask = a->sipsize - 1;
|
||||
int j = (a->idx - a->outsize) & mask;
|
||||
int size = a->sipsize - j;
|
||||
if (size >= a->outsize)
|
||||
memcpy (a->sipout, &(a->sipbuff[2 * j]), a->outsize * sizeof (dcomplex));
|
||||
else
|
||||
{
|
||||
memcpy (a->sipout, &(a->sipbuff[2 * j]), size * sizeof (dcomplex));
|
||||
memcpy (&(a->sipout[2 * size]), a->sipbuff, (a->outsize - size) * sizeof (dcomplex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SIPHON::sip_spectrum (SIPHON *a)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < a->fftsize; i++)
|
||||
{
|
||||
a->sipout[2 * i + 0] *= a->window[i];
|
||||
a->sipout[2 * i + 1] *= a->window[i];
|
||||
}
|
||||
fftw_execute (a->sipplan);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SIPHON::GetaSipF (RXA& rxa, float* out, int size)
|
||||
{ // return raw samples as floats
|
||||
SIPHON *a=rxa.sip1.p;
|
||||
int i;
|
||||
a->update.lock();
|
||||
a->outsize = size;
|
||||
suck (a);
|
||||
a->update.unlock();
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
out[i] = (float)a->sipout[2 * i + 0];
|
||||
}
|
||||
}
|
||||
|
||||
void SIPHON::GetaSipF1 (RXA& rxa, float* out, int size)
|
||||
{ // return raw samples as floats
|
||||
SIPHON *a=rxa.sip1.p;
|
||||
int i;
|
||||
a->update.lock();
|
||||
a->outsize = size;
|
||||
suck (a);
|
||||
a->update.unlock();
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
out[2 * i + 0] = (float)a->sipout[2 * i + 0];
|
||||
out[2 * i + 1] = (float)a->sipout[2 * i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SIPHON::SetSipPosition (TXA& txa, int pos)
|
||||
{
|
||||
SIPHON *a = txa.sip1.p;
|
||||
a->update.lock();
|
||||
a->position = pos;
|
||||
a->update.unlock();
|
||||
}
|
||||
|
||||
void SIPHON::SetSipMode (TXA& txa, int mode)
|
||||
{
|
||||
SIPHON *a = txa.sip1.p;
|
||||
a->update.lock();
|
||||
a->mode = mode;
|
||||
a->update.unlock();
|
||||
}
|
||||
|
||||
void SIPHON::SetSipDisplay (TXA& txa, int disp)
|
||||
{
|
||||
SIPHON *a = txa.sip1.p;
|
||||
a->update.lock();
|
||||
a->disp = disp;
|
||||
a->update.unlock();
|
||||
}
|
||||
|
||||
void SIPHON::GetaSipF (TXA& txa, float* out, int size)
|
||||
{ // return raw samples as floats
|
||||
SIPHON *a = txa.sip1.p;
|
||||
int i;
|
||||
a->update.lock();
|
||||
a->outsize = size;
|
||||
suck (a);
|
||||
a->update.unlock();
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
out[i] = (float)a->sipout[2 * i + 0];
|
||||
}
|
||||
}
|
||||
|
||||
void SIPHON::GetaSipF1 (TXA& txa, float* out, int size)
|
||||
{ // return raw samples as floats
|
||||
SIPHON *a = txa.sip1.p;
|
||||
int i;
|
||||
a->update.lock();
|
||||
a->outsize = size;
|
||||
suck (a);
|
||||
a->update.unlock();
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
out[2 * i + 0] = (float)a->sipout[2 * i + 0];
|
||||
out[2 * i + 1] = (float)a->sipout[2 * i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
void SIPHON::SetSipSpecmode (TXA& txa, int mode)
|
||||
{
|
||||
SIPHON *a = txa.sip1.p;
|
||||
if (mode == 0)
|
||||
a->specmode = 0;
|
||||
else
|
||||
a->specmode = 1;
|
||||
}
|
||||
|
||||
void SIPHON::GetSpecF1 (TXA& txa, float* out)
|
||||
{ // return spectrum magnitudes in dB
|
||||
SIPHON *a = txa.sip1.p;
|
||||
int i, j, mid, m, n;
|
||||
a->update.lock();
|
||||
a->outsize = a->fftsize;
|
||||
suck (a);
|
||||
a->update.unlock();
|
||||
sip_spectrum (a);
|
||||
mid = a->fftsize / 2;
|
||||
if (a->specmode != 1)
|
||||
// swap the halves of the spectrum
|
||||
for (i = 0, j = mid; i < mid; i++, j++)
|
||||
{
|
||||
out[i] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * j + 0] * a->specout[2 * j + 0] + a->specout[2 * j + 1] * a->specout[2 * j + 1] + 1.0e-60));
|
||||
out[j] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * i + 0] * a->specout[2 * i + 0] + a->specout[2 * i + 1] * a->specout[2 * i + 1] + 1.0e-60));
|
||||
}
|
||||
else
|
||||
// mirror each half of the spectrum in-place
|
||||
for (i = 0, j = mid - 1, m = mid, n = a->fftsize - 1; i < mid; i++, j--, m++, n--)
|
||||
{
|
||||
out[i] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * j + 0] * a->specout[2 * j + 0] + a->specout[2 * j + 1] * a->specout[2 * j + 1] + 1.0e-60));
|
||||
out[m] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * n + 0] * a->specout[2 * n + 0] + a->specout[2 * n + 1] * a->specout[2 * n + 1] + 1.0e-60));
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* CALLS FOR EXTERNAL USE *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
/*
|
||||
#define MAX_EXT_SIPHONS (2) // maximum number of Siphons called from outside wdsp
|
||||
__declspec (align (16)) SIPHON psiphon[MAX_EXT_SIPHONS]; // array of pointers for Siphons used EXTERNAL to wdsp
|
||||
|
||||
|
||||
PORT
|
||||
void create_siphonEXT (int id, int run, int insize, int sipsize, int fftsize, int specmode)
|
||||
{
|
||||
psiphon[id] = create_siphon (run, 0, 0, 0, insize, 0, sipsize, fftsize, specmode);
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_siphonEXT (int id)
|
||||
{
|
||||
destroy_siphon (psiphon[id]);
|
||||
}
|
||||
|
||||
PORT
|
||||
void flush_siphonEXT (int id)
|
||||
{
|
||||
flush_siphon (psiphon[id]);
|
||||
}
|
||||
|
||||
PORT
|
||||
void xsiphonEXT (int id, double* buff)
|
||||
{
|
||||
SIPHON a = psiphon[id];
|
||||
a->in = buff;
|
||||
xsiphon (a, 0);
|
||||
}
|
||||
|
||||
PORT
|
||||
void GetaSipF1EXT (int id, float* out, int size)
|
||||
{ // return raw samples as floats
|
||||
SIPHON a = psiphon[id];
|
||||
int i;
|
||||
a->update.lock();
|
||||
a->outsize = size;
|
||||
suck (a);
|
||||
a->update.unlock();
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
out[2 * i + 0] = (float)a->sipout[2 * i + 0];
|
||||
out[2 * i + 1] = (float)a->sipout[2 * i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetSiphonInsize (int id, int size)
|
||||
{
|
||||
SIPHON a = psiphon[id];
|
||||
a->update.lock();
|
||||
a->insize = size;
|
||||
a->update.unlock();
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
108
wdsp/siphon.hpp
Normal file
108
wdsp/siphon.hpp
Normal file
@ -0,0 +1,108 @@
|
||||
/* siphon.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
// 'siphon' collects samples in a buffer. These samples can then be PULLED from the buffer
|
||||
// in either raw or FFT'd form.
|
||||
|
||||
#ifndef wdsp_siphon_h
|
||||
#define wdsp_siphon_h
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "export.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RXA;
|
||||
class TXA;
|
||||
|
||||
class WDSP_API SIPHON
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
int position;
|
||||
int mode;
|
||||
int disp;
|
||||
int insize;
|
||||
double* in;
|
||||
int sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!!
|
||||
double* sipbuff;
|
||||
int outsize;
|
||||
int idx;
|
||||
double* sipout;
|
||||
int fftsize;
|
||||
double* specout;
|
||||
std::atomic<long> specmode;
|
||||
fftw_plan sipplan;
|
||||
double* window;
|
||||
QRecursiveMutex update;
|
||||
|
||||
static SIPHON* create_siphon (
|
||||
int run,
|
||||
int position,
|
||||
int mode,
|
||||
int disp,
|
||||
int insize,
|
||||
double* in,
|
||||
int sipsize,
|
||||
int fftsize,
|
||||
int specmode
|
||||
);
|
||||
static void destroy_siphon (SIPHON *a);
|
||||
static void flush_siphon (SIPHON *a);
|
||||
static void xsiphon (SIPHON *a, int pos);
|
||||
static void setBuffers_siphon (SIPHON *a, double* in);
|
||||
static void setSamplerate_siphon (SIPHON *a, int rate);
|
||||
static void setSize_siphon (SIPHON *a, int size);
|
||||
// RXA Properties
|
||||
static void GetaSipF (RXA& rxa, float* out, int size);
|
||||
static void GetaSipF1 (RXA& rxa, float* out, int size);
|
||||
// TXA Properties
|
||||
static void SetSipPosition (TXA& txa, int pos);
|
||||
static void SetSipMode (TXA& txa, int mode);
|
||||
static void SetSipDisplay (TXA& txa, int disp);
|
||||
static void GetaSipF (TXA& txa, float* out, int size);
|
||||
static void GetaSipF1 (TXA& txa, float* out, int size);
|
||||
static void GetSpecF1 (TXA& txa, float* out);
|
||||
static void SetSipSpecmode (TXA& txa, int mode);
|
||||
// Calls for External Use
|
||||
// static void create_siphonEXT (int id, int run, int insize, int sipsize, int fftsize, int specmode);
|
||||
// static void destroy_siphonEXT (int id);
|
||||
// static void xsiphonEXT (int id, double* buff);
|
||||
// static void SetSiphonInsize (int id, int size);
|
||||
|
||||
private:
|
||||
static void build_window (SIPHON *a);
|
||||
static void suck (SIPHON *a);
|
||||
static void sip_spectrum (SIPHON *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
200
wdsp/slew.cpp
Normal file
200
wdsp/slew.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
/* slew.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 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 "slew.hpp"
|
||||
#include "TXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
enum _USLEW
|
||||
{
|
||||
BEGIN,
|
||||
WAIT,
|
||||
UP,
|
||||
ON
|
||||
};
|
||||
|
||||
void USLEW::calc_uslew (USLEW *a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
a->runmode = 0;
|
||||
a->state = BEGIN;
|
||||
a->count = 0;
|
||||
a->ndelup = (int)(a->tdelay * a->rate);
|
||||
a->ntup = (int)(a->tupslew * a->rate);
|
||||
a->cup = new double[a->ntup + 1]; // (double *) malloc0 ((a->ntup + 1) * sizeof (double));
|
||||
delta = PI / (double)a->ntup;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntup; i++)
|
||||
{
|
||||
a->cup[i] = 0.5 * (1.0 - cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
*a->ch_upslew &= ~((long)1); // InterlockedBitTestAndReset (a->ch_upslew, 0);
|
||||
}
|
||||
|
||||
void USLEW::decalc_uslew (USLEW *a)
|
||||
{
|
||||
delete[] (a->cup);
|
||||
}
|
||||
|
||||
USLEW* USLEW::create_uslew (TXA *txa, std::atomic<long> *ch_upslew, int size, double* in, double* out, double rate, double tdelay, double tupslew)
|
||||
{
|
||||
USLEW *a = new USLEW;
|
||||
a->txa = txa;
|
||||
a->ch_upslew = ch_upslew;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = rate;
|
||||
a->tdelay = tdelay;
|
||||
a->tupslew = tupslew;
|
||||
calc_uslew (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void USLEW::destroy_uslew (USLEW *a)
|
||||
{
|
||||
decalc_uslew (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void USLEW::flush_uslew (USLEW *a)
|
||||
{
|
||||
a->state = BEGIN;
|
||||
a->runmode = 0;
|
||||
*a->ch_upslew &= ~1L; //InterlockedBitTestAndReset (a->ch_upslew, 0);
|
||||
}
|
||||
|
||||
void USLEW::xuslew (USLEW *a)
|
||||
{
|
||||
if (!a->runmode && TXA::UslewCheck (*a->txa))
|
||||
a->runmode = 1;
|
||||
|
||||
long upslew = *a->ch_upslew;
|
||||
*a->ch_upslew = 1L;
|
||||
if (a->runmode && upslew) //_InterlockedAnd (a->ch_upslew, 1))
|
||||
{
|
||||
int i;
|
||||
double I, Q;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
I = a->in[2 * i + 0];
|
||||
Q = a->in[2 * i + 1];
|
||||
switch (a->state)
|
||||
{
|
||||
case BEGIN:
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
if ((I != 0.0) || (Q != 0.0))
|
||||
{
|
||||
if (a->ndelup > 0)
|
||||
{
|
||||
a->state = WAIT;
|
||||
a->count = a->ndelup;
|
||||
}
|
||||
else if (a->ntup > 0)
|
||||
{
|
||||
a->state = UP;
|
||||
a->count = a->ntup;
|
||||
}
|
||||
else
|
||||
a->state = ON;
|
||||
}
|
||||
break;
|
||||
case WAIT:
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
if (a->count-- == 0)
|
||||
{
|
||||
if (a->ntup > 0)
|
||||
{
|
||||
a->state = UP;
|
||||
a->count = a->ntup;
|
||||
}
|
||||
else
|
||||
a->state = ON;
|
||||
}
|
||||
break;
|
||||
case UP:
|
||||
a->out[2 * i + 0] = I * a->cup[a->ntup - a->count];
|
||||
a->out[2 * i + 1] = Q * a->cup[a->ntup - a->count];
|
||||
if (a->count-- == 0)
|
||||
a->state = ON;
|
||||
break;
|
||||
case ON:
|
||||
a->out[2 * i + 0] = I;
|
||||
a->out[2 * i + 1] = Q;
|
||||
*a->ch_upslew &= ~((long)1); // InterlockedBitTestAndReset (a->ch_upslew, 0);
|
||||
a->runmode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void USLEW::setBuffers_uslew (USLEW *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void USLEW::setSamplerate_uslew (USLEW *a, int rate)
|
||||
{
|
||||
decalc_uslew (a);
|
||||
a->rate = rate;
|
||||
calc_uslew (a);
|
||||
}
|
||||
|
||||
void USLEW::setSize_uslew (USLEW *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_uslew (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void USLEW::SetuSlewTime (TXA& txa, double time)
|
||||
{
|
||||
// NOTE: 'time' is in seconds
|
||||
txa.csDSP.lock();
|
||||
USLEW *a = txa.uslew.p;
|
||||
decalc_uslew (a);
|
||||
a->tupslew = time;
|
||||
calc_uslew (a);
|
||||
txa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
74
wdsp/slew.hpp
Normal file
74
wdsp/slew.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
/* slew.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_slew_h
|
||||
#define wdsp_slew_h
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class TXA;
|
||||
|
||||
class WDSP_API USLEW
|
||||
{
|
||||
public:
|
||||
TXA *txa;
|
||||
std::atomic<long> *ch_upslew;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double tdelay;
|
||||
double tupslew;
|
||||
int runmode;
|
||||
int state;
|
||||
int count;
|
||||
int ndelup;
|
||||
int ntup;
|
||||
double* cup;
|
||||
|
||||
static USLEW* create_uslew (TXA *txa, std::atomic<long> *ch_upslew, int size, double* in, double* out, double rate, double tdelay, double tupslew);
|
||||
static void destroy_uslew (USLEW *a);
|
||||
static void flush_uslew (USLEW *a);
|
||||
static void xuslew (USLEW *a);
|
||||
static void setBuffers_uslew (USLEW *a, double* in, double* out);
|
||||
static void setSamplerate_uslew (USLEW *a, int rate);
|
||||
static void setSize_uslew (USLEW *a, int size);
|
||||
// TXA Properties
|
||||
static void SetuSlewTime (TXA& txa, double time);
|
||||
|
||||
private:
|
||||
static void calc_uslew (USLEW *a);
|
||||
static void decalc_uslew (USLEW *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
933
wdsp/snb.cpp
Normal file
933
wdsp/snb.cpp
Normal file
@ -0,0 +1,933 @@
|
||||
/* snb.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "resample.hpp"
|
||||
#include "lmath.hpp"
|
||||
#include "firmin.hpp"
|
||||
#include "nbp.hpp"
|
||||
#include "amd.hpp"
|
||||
#include "anf.hpp"
|
||||
#include "anr.hpp"
|
||||
#include "emnr.hpp"
|
||||
#include "snb.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
|
||||
#define MAXIMP 256
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void SNBA::calc_snba (SNBA *d)
|
||||
{
|
||||
if (d->inrate >= d->internalrate)
|
||||
d->isize = d->bsize / (d->inrate / d->internalrate);
|
||||
else
|
||||
d->isize = d->bsize * (d->internalrate / d->inrate);
|
||||
d->inbuff = new double[d->isize * 2]; // (double *) malloc0 (d->isize * sizeof (complex));
|
||||
d->outbuff = new double[d->isize * 2]; // (double *) malloc0 (d->isize * sizeof (complex));
|
||||
if (d->inrate != d->internalrate)
|
||||
d->resamprun = 1;
|
||||
else
|
||||
d->resamprun = 0;
|
||||
d->inresamp = RESAMPLE::create_resample (d->resamprun, d->bsize, d->in, d->inbuff, d->inrate, d->internalrate, 0.0, 0, 2.0);
|
||||
RESAMPLE::setFCLow_resample (d->inresamp, 250.0);
|
||||
d->outresamp = RESAMPLE::create_resample (d->resamprun, d->isize, d->outbuff, d->out, d->internalrate, d->inrate, 0.0, 0, 2.0);
|
||||
RESAMPLE::setFCLow_resample (d->outresamp, 200.0);
|
||||
d->incr = d->xsize / d->ovrlp;
|
||||
if (d->incr > d->isize)
|
||||
d->iasize = d->incr;
|
||||
else
|
||||
d->iasize = d->isize;
|
||||
d->iainidx = 0;
|
||||
d->iaoutidx = 0;
|
||||
d->inaccum = new double[d->isize]; // (double *) malloc0 (d->iasize * sizeof (double));
|
||||
d->nsamps = 0;
|
||||
if (d->incr > d->isize)
|
||||
{
|
||||
d->oasize = d->incr;
|
||||
d->oainidx = 0;
|
||||
d->oaoutidx = d->isize;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->oasize = d->isize;
|
||||
d->oainidx = 0;
|
||||
d->oaoutidx = 0;
|
||||
}
|
||||
d->init_oaoutidx = d->oaoutidx;
|
||||
d->outaccum = new double[d->oasize]; // (double *) malloc0 (d->oasize * sizeof (double));
|
||||
}
|
||||
|
||||
SNBA* SNBA::create_snba (
|
||||
int run,
|
||||
double* in,
|
||||
double* out,
|
||||
int inrate,
|
||||
int internalrate,
|
||||
int bsize,
|
||||
int ovrlp,
|
||||
int xsize,
|
||||
int asize,
|
||||
int npasses,
|
||||
double k1,
|
||||
double k2,
|
||||
int b,
|
||||
int pre,
|
||||
int post,
|
||||
double pmultmin,
|
||||
double out_low_cut,
|
||||
double out_high_cut
|
||||
)
|
||||
{
|
||||
SNBA *d = new SNBA;
|
||||
d->run = run;
|
||||
d->in = in;
|
||||
d->out = out;
|
||||
d->inrate = inrate;
|
||||
d->internalrate = internalrate;
|
||||
d->bsize = bsize;
|
||||
d->ovrlp = ovrlp;
|
||||
d->xsize = xsize;
|
||||
d->exec.asize = asize;
|
||||
d->exec.npasses = npasses;
|
||||
d->sdet.k1 = k1;
|
||||
d->sdet.k2 = k2;
|
||||
d->sdet.b = b;
|
||||
d->sdet.pre = pre;
|
||||
d->sdet.post = post;
|
||||
d->scan.pmultmin = pmultmin;
|
||||
d->out_low_cut = out_low_cut;
|
||||
d->out_high_cut = out_high_cut;
|
||||
|
||||
calc_snba (d);
|
||||
|
||||
d->xbase = new double[2 * d->xsize]; // (double *) malloc0 (2 * d->xsize * sizeof (double));
|
||||
d->xaux = d->xbase + d->xsize;
|
||||
d->exec.a = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof (double));
|
||||
d->exec.v = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof (double));
|
||||
d->exec.detout = new int[d->xsize]; // (int *) malloc0 (d->xsize * sizeof (int));
|
||||
d->exec.savex = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof (double));
|
||||
d->exec.xHout = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof (double));
|
||||
d->exec.unfixed = new int32_t[d->xsize]; // (int *) malloc0 (d->xsize * sizeof (int));
|
||||
d->sdet.vp = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof (double));
|
||||
d->sdet.vpwr = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof (double));
|
||||
|
||||
d->wrk.xHat_a1rows_max = d->xsize + d->exec.asize;
|
||||
d->wrk.xHat_a2cols_max = d->xsize + 2 * d->exec.asize;
|
||||
d->wrk.xHat_r = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof(double));
|
||||
d->wrk.xHat_ATAI = new double[d->xsize * d->xsize]; // (double *) malloc0 (d->xsize * d->xsize * sizeof(double));
|
||||
d->wrk.xHat_A1 = new double[d->wrk.xHat_a1rows_max * d->xsize]; // (double *) malloc0 (d->wrk.xHat_a1rows_max * d->xsize * sizeof(double));
|
||||
d->wrk.xHat_A2 = new double[d->wrk.xHat_a1rows_max * d->wrk.xHat_a2cols_max]; // (double *) malloc0 (d->wrk.xHat_a1rows_max * d->wrk.xHat_a2cols_max * sizeof(double));
|
||||
d->wrk.xHat_P1 = new double[d->xsize * d->wrk.xHat_a2cols_max]; // (double *) malloc0 (d->xsize * d->wrk.xHat_a2cols_max * sizeof(double));
|
||||
d->wrk.xHat_P2 = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof(double));
|
||||
d->wrk.trI_y = new double[d->xsize - 1]; // (double *) malloc0 ((d->xsize - 1) * sizeof(double));
|
||||
d->wrk.trI_v = new double[d->xsize - 1]; // (double *) malloc0 ((d->xsize - 1) * sizeof(double));
|
||||
d->wrk.dR_z = new double[d->xsize - 2]; // (double *) malloc0 ((d->xsize - 2) * sizeof(double));
|
||||
d->wrk.asolve_r = new double[d->exec.asize + 1]; // (double *) malloc0 ((d->exec.asize + 1) * sizeof(double));
|
||||
d->wrk.asolve_z = new double[d->exec.asize + 1]; // (double *) malloc0 ((d->exec.asize + 1) * sizeof(double));
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void SNBA::decalc_snba (SNBA *d)
|
||||
{
|
||||
RESAMPLE::destroy_resample (d->outresamp);
|
||||
RESAMPLE::destroy_resample (d->inresamp);
|
||||
delete[] (d->outbuff);
|
||||
delete[] (d->inbuff);
|
||||
delete[] (d->outaccum);
|
||||
delete[] (d->inaccum);
|
||||
}
|
||||
|
||||
void SNBA::destroy_snba (SNBA *d)
|
||||
{
|
||||
delete[] (d->wrk.xHat_r);
|
||||
delete[] (d->wrk.xHat_ATAI);
|
||||
delete[] (d->wrk.xHat_A1);
|
||||
delete[] (d->wrk.xHat_A2);
|
||||
delete[] (d->wrk.xHat_P1);
|
||||
delete[] (d->wrk.xHat_P2);
|
||||
delete[] (d->wrk.trI_y);
|
||||
delete[] (d->wrk.trI_v);
|
||||
delete[] (d->wrk.dR_z);
|
||||
delete[] (d->wrk.asolve_r);
|
||||
delete[] (d->wrk.asolve_z);
|
||||
|
||||
delete[] (d->sdet.vpwr);
|
||||
delete[] (d->sdet.vp);
|
||||
delete[] (d->exec.unfixed);
|
||||
delete[] (d->exec.xHout);
|
||||
delete[] (d->exec.savex);
|
||||
delete[] (d->exec.detout);
|
||||
delete[] (d->exec.v);
|
||||
delete[] (d->exec.a);
|
||||
|
||||
delete[] (d->xbase);
|
||||
|
||||
decalc_snba (d);
|
||||
|
||||
delete (d);
|
||||
}
|
||||
|
||||
void SNBA::flush_snba (SNBA *d)
|
||||
{
|
||||
d->iainidx = 0;
|
||||
d->iaoutidx = 0;
|
||||
d->nsamps = 0;
|
||||
d->oainidx = 0;
|
||||
d->oaoutidx = d->init_oaoutidx;
|
||||
|
||||
memset (d->inaccum, 0, d->iasize * sizeof (double));
|
||||
memset (d->outaccum, 0, d->oasize * sizeof (double));
|
||||
memset (d->xaux, 0, d->xsize * sizeof (double));
|
||||
memset (d->exec.a, 0, d->xsize * sizeof (double));
|
||||
memset (d->exec.v, 0, d->xsize * sizeof (double));
|
||||
memset (d->exec.detout, 0, d->xsize * sizeof (int));
|
||||
memset (d->exec.savex, 0, d->xsize * sizeof (double));
|
||||
memset (d->exec.xHout, 0, d->xsize * sizeof (double));
|
||||
memset (d->exec.unfixed, 0, d->xsize * sizeof (int));
|
||||
memset (d->sdet.vp, 0, d->xsize * sizeof (double));
|
||||
memset (d->sdet.vpwr, 0, d->xsize * sizeof (double));
|
||||
|
||||
memset (d->inbuff, 0, d->isize * sizeof (dcomplex));
|
||||
memset (d->outbuff, 0, d->isize * sizeof (dcomplex));
|
||||
RESAMPLE::flush_resample (d->inresamp);
|
||||
RESAMPLE::flush_resample (d->outresamp);
|
||||
}
|
||||
|
||||
void SNBA::setBuffers_snba (SNBA *a, double* in, double* out)
|
||||
{
|
||||
decalc_snba (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_snba (a);
|
||||
}
|
||||
|
||||
void SNBA::setSamplerate_snba (SNBA *a, int rate)
|
||||
{
|
||||
decalc_snba (a);
|
||||
a->inrate = rate;
|
||||
calc_snba (a);
|
||||
}
|
||||
|
||||
void SNBA::setSize_snba (SNBA *a, int size)
|
||||
{
|
||||
decalc_snba (a);
|
||||
a->bsize = size;
|
||||
calc_snba (a);
|
||||
}
|
||||
|
||||
void SNBA::ATAc0 (int n, int nr, double* A, double* r)
|
||||
{
|
||||
int i, j;
|
||||
memset(r, 0, n * sizeof (double));
|
||||
for (i = 0; i < n; i++)
|
||||
for (j = 0; j < nr; j++)
|
||||
r[i] += A[j * n + i] * A[j * n + 0];
|
||||
}
|
||||
|
||||
void SNBA::multA1TA2(double* a1, double* a2, int m, int n, int q, double* c)
|
||||
{
|
||||
int i, j, k;
|
||||
int p = q - m;
|
||||
memset (c, 0, m * n * sizeof (double));
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
for (j = 0; j < n; j++)
|
||||
{
|
||||
if (j < p)
|
||||
{
|
||||
for (k = i; k <= std::min(i + p, j); k++)
|
||||
c[i * n + j] += a1[k * m + i] * a2[k * n + j];
|
||||
}
|
||||
if (j >= n - p)
|
||||
{
|
||||
for (k = std::max(i, q - (n - j)); k <= i + p; k++)
|
||||
c[i * n + j] += a1[k * m + i] * a2[k * n + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SNBA::multXKE(double* a, double* xk, int m, int q, int p, double* vout)
|
||||
{
|
||||
int i, k;
|
||||
memset (vout, 0, m * sizeof (double));
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
for (k = i; k < p; k++)
|
||||
vout[i] += a[i * q + k] * xk[k];
|
||||
for (k = q - p; k <= q - m + i; k++)
|
||||
vout[i] += a[i * q + k] * xk[k];
|
||||
}
|
||||
}
|
||||
|
||||
void SNBA::multAv(double* a, double* v, int m, int q, double* vout)
|
||||
{
|
||||
int i, k;
|
||||
memset (vout, 0, m * sizeof (double));
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
for (k = 0; k < q; k++)
|
||||
vout[i] += a[i * q + k] * v[k];
|
||||
}
|
||||
}
|
||||
|
||||
void SNBA::xHat(
|
||||
int xusize,
|
||||
int asize,
|
||||
double* xk,
|
||||
double* a,
|
||||
double* xout,
|
||||
double* r,
|
||||
double* ATAI,
|
||||
double* A1,
|
||||
double* A2,
|
||||
double* P1,
|
||||
double* P2,
|
||||
double* trI_y,
|
||||
double* trI_v,
|
||||
double* dR_z
|
||||
)
|
||||
{
|
||||
int i, j, k;
|
||||
int a1rows = xusize + asize;
|
||||
int a2cols = xusize + 2 * asize;
|
||||
memset (r, 0, xusize * sizeof(double)); // work space
|
||||
memset (ATAI, 0, xusize * xusize * sizeof(double)); // work space
|
||||
memset (A1, 0, a1rows * xusize * sizeof(double)); // work space
|
||||
memset (A2, 0, a1rows * a2cols * sizeof(double)); // work space
|
||||
memset (P1, 0, xusize * a2cols * sizeof(double)); // work space
|
||||
memset (P2, 0, xusize * sizeof(double)); // work space
|
||||
|
||||
for (i = 0; i < xusize; i++)
|
||||
{
|
||||
A1[i * xusize + i] = 1.0;
|
||||
k = i + 1;
|
||||
for (j = k; j < k + asize; j++)
|
||||
A1[j * xusize + i] = - a[j - k];
|
||||
}
|
||||
|
||||
for (i = 0; i < asize; i++)
|
||||
{
|
||||
for (k = asize - i - 1, j = 0; k < asize; k++, j++)
|
||||
A2[j * a2cols + i] = a[k];
|
||||
}
|
||||
for (i = asize + xusize; i < 2 * asize + xusize; i++)
|
||||
{
|
||||
A2[(i - asize) * a2cols + i] = - 1.0;
|
||||
for (j = i - asize + 1, k = 0; j < xusize + asize; j++, k++)
|
||||
A2[j * a2cols + i] = a[k];
|
||||
}
|
||||
|
||||
ATAc0(xusize, xusize + asize, A1, r);
|
||||
LMath::trI(xusize, r, ATAI, trI_y, trI_v, dR_z);
|
||||
multA1TA2(A1, A2, xusize, 2 * asize + xusize, xusize + asize, P1);
|
||||
multXKE(P1, xk, xusize, xusize + 2 * asize, asize, P2);
|
||||
multAv(ATAI, P2, xusize, xusize, xout);
|
||||
}
|
||||
|
||||
void SNBA::invf(int xsize, int asize, double* a, double* x, double* v)
|
||||
{
|
||||
int i, j;
|
||||
memset (v, 0, xsize * sizeof (double));
|
||||
for (i = asize; i < xsize - asize; i++)
|
||||
{
|
||||
for (j = 0; j < asize; j++)
|
||||
v[i] += a[j] * (x[i - 1 - j] + x[i + 1 + j]);
|
||||
v[i] = x[i] - 0.5 * v[i];
|
||||
}
|
||||
for (i = xsize - asize; i < xsize; i++)
|
||||
{
|
||||
for (j = 0; j < asize; j++)
|
||||
v[i] += a[j] * x[i - 1 - j];
|
||||
v[i] = x[i] - v[i];
|
||||
}
|
||||
}
|
||||
|
||||
void SNBA::det(SNBA *d, int asize, double* v, int* detout)
|
||||
{
|
||||
int i, j;
|
||||
double medpwr, t1, t2;
|
||||
int bstate, bcount, bsamp;
|
||||
for (i = asize, j = 0; i < d->xsize; i++, j++)
|
||||
{
|
||||
d->sdet.vpwr[i] = v[i] * v[i];
|
||||
d->sdet.vp[j] = d->sdet.vpwr[i];
|
||||
}
|
||||
LMath::median(d->xsize - asize, d->sdet.vp, &medpwr);
|
||||
t1 = d->sdet.k1 * medpwr;
|
||||
t2 = 0.0;
|
||||
for (i = asize; i < d->xsize; i++)
|
||||
{
|
||||
if (d->sdet.vpwr[i] <= t1)
|
||||
t2 += d->sdet.vpwr[i];
|
||||
else if (d->sdet.vpwr[i] <= 2.0 * t1)
|
||||
t2 += 2.0 * t1 - d->sdet.vpwr[i];
|
||||
}
|
||||
t2 *= d->sdet.k2 / (double)(d->xsize - asize);
|
||||
for (i = asize; i < d->xsize; i++)
|
||||
{
|
||||
if (d->sdet.vpwr[i] > t2)
|
||||
detout[i] = 1;
|
||||
else
|
||||
detout[i] = 0;
|
||||
}
|
||||
bstate = 0;
|
||||
bcount = 0;
|
||||
bsamp = 0;
|
||||
for (i = asize; i < d->xsize; i++)
|
||||
{
|
||||
switch (bstate)
|
||||
{
|
||||
case 0:
|
||||
if (detout[i] == 1) bstate = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (detout[i] == 0)
|
||||
{
|
||||
bstate = 2;
|
||||
bsamp = i;
|
||||
bcount = 1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
++bcount;
|
||||
if (bcount > d->sdet.b)
|
||||
if (detout[i] == 1)
|
||||
bstate = 1;
|
||||
else
|
||||
bstate = 0;
|
||||
else if (detout[i] == 1)
|
||||
{
|
||||
for (j = bsamp; j < bsamp + bcount - 1; j++)
|
||||
detout[j] = 1;
|
||||
bstate = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = asize; i < d->xsize; i++)
|
||||
{
|
||||
if (detout[i] == 1)
|
||||
{
|
||||
for (j = i - 1; j > i - 1 - d->sdet.pre; j--)
|
||||
if (j >= asize) detout[j] = 1;
|
||||
}
|
||||
}
|
||||
for (i = d->xsize - 1; i >= asize; i--)
|
||||
{
|
||||
if (detout[i] == 1)
|
||||
{
|
||||
for (j = i + 1; j < i + 1 + d->sdet.post; j++)
|
||||
if (j < d->xsize) detout[j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SNBA::scanFrame(
|
||||
int xsize,
|
||||
int pval,
|
||||
double pmultmin,
|
||||
int* det,
|
||||
int* bimp,
|
||||
int* limp,
|
||||
int* befimp,
|
||||
int* aftimp,
|
||||
int* p_opt,
|
||||
int* next
|
||||
)
|
||||
{
|
||||
int inflag = 0;
|
||||
int i = 0, j = 0, k = 0;
|
||||
int nimp = 0;
|
||||
double td;
|
||||
int ti;
|
||||
double merit[MAXIMP] = { 0 };
|
||||
int nextlist[MAXIMP];
|
||||
memset (befimp, 0, MAXIMP * sizeof (int));
|
||||
memset (aftimp, 0, MAXIMP * sizeof (int));
|
||||
while (i < xsize && nimp < MAXIMP)
|
||||
{
|
||||
if (det[i] == 1 && inflag == 0)
|
||||
{
|
||||
inflag = 1;
|
||||
bimp[nimp] = i;
|
||||
limp[nimp] = 1;
|
||||
nimp++;
|
||||
}
|
||||
else if (det[i] == 1)
|
||||
{
|
||||
limp[nimp - 1]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
inflag = 0;
|
||||
befimp[nimp]++;
|
||||
if (nimp > 0)
|
||||
aftimp[nimp - 1]++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
for (i = 0; i < nimp; i++)
|
||||
{
|
||||
if (befimp[i] < aftimp[i])
|
||||
p_opt[i] = befimp[i];
|
||||
else
|
||||
p_opt[i] = aftimp[i];
|
||||
if (p_opt[i] > pval)
|
||||
p_opt[i] = pval;
|
||||
if (p_opt[i] < (int)(pmultmin * limp[i]))
|
||||
p_opt[i] = -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nimp; i++)
|
||||
{
|
||||
merit[i] = (double)p_opt[i] / (double)limp[i];
|
||||
nextlist[i] = i;
|
||||
}
|
||||
for (j = 0; j < nimp - 1; j++)
|
||||
{
|
||||
for (k = 0; k < nimp - j - 1; k++)
|
||||
{
|
||||
if (merit[k] < merit[k + 1])
|
||||
{
|
||||
td = merit[k];
|
||||
ti = nextlist[k];
|
||||
merit[k] = merit[k + 1];
|
||||
nextlist[k] = nextlist[k + 1];
|
||||
merit[k + 1] = td;
|
||||
nextlist[k + 1] = ti;
|
||||
}
|
||||
}
|
||||
}
|
||||
i = 1;
|
||||
if (nimp > 0)
|
||||
while (merit[i] == merit[0] && i < nimp) i++;
|
||||
for (j = 0; j < i - 1; j++)
|
||||
{
|
||||
for (k = 0; k < i - j - 1; k++)
|
||||
{
|
||||
if (limp[nextlist[k]] < limp[nextlist[k + 1]])
|
||||
{
|
||||
td = merit[k];
|
||||
ti = nextlist[k];
|
||||
merit[k] = merit[k + 1];
|
||||
nextlist[k] = nextlist[k + 1];
|
||||
merit[k + 1] = td;
|
||||
nextlist[k + 1] = ti;
|
||||
}
|
||||
}
|
||||
}
|
||||
*next = nextlist[0];
|
||||
return nimp;
|
||||
}
|
||||
|
||||
void SNBA::execFrame(SNBA *d, double* x)
|
||||
{
|
||||
int i, k;
|
||||
int pass;
|
||||
int nimp;
|
||||
int bimp[MAXIMP];
|
||||
int limp[MAXIMP];
|
||||
int befimp[MAXIMP];
|
||||
int aftimp[MAXIMP];
|
||||
int p_opt[MAXIMP];
|
||||
int next = 0;
|
||||
int p;
|
||||
memcpy (d->exec.savex, x, d->xsize * sizeof (double));
|
||||
LMath::asolve(d->xsize, d->exec.asize, x, d->exec.a, d->wrk.asolve_r, d->wrk.asolve_z);
|
||||
invf(d->xsize, d->exec.asize, d->exec.a, x, d->exec.v);
|
||||
det(d, d->exec.asize, d->exec.v, d->exec.detout);
|
||||
for (i = 0; i < d->xsize; i++)
|
||||
{
|
||||
if (d->exec.detout[i] != 0)
|
||||
x[i] = 0.0;
|
||||
}
|
||||
nimp = scanFrame(d->xsize, d->exec.asize, d->scan.pmultmin, d->exec.detout, bimp, limp, befimp, aftimp, p_opt, &next);
|
||||
for (pass = 0; pass < d->exec.npasses; pass++)
|
||||
{
|
||||
memcpy (d->exec.unfixed, d->exec.detout, d->xsize * sizeof (int));
|
||||
for (k = 0; k < nimp; k++)
|
||||
{
|
||||
if (k > 0)
|
||||
scanFrame(d->xsize, d->exec.asize, d->scan.pmultmin, d->exec.unfixed, bimp, limp, befimp, aftimp, p_opt, &next);
|
||||
|
||||
if ((p = p_opt[next]) > 0)
|
||||
{
|
||||
LMath::asolve(d->xsize, p, x, d->exec.a, d->wrk.asolve_r, d->wrk.asolve_z);
|
||||
xHat(limp[next], p, &x[bimp[next] - p], d->exec.a, d->exec.xHout,
|
||||
d->wrk.xHat_r, d->wrk.xHat_ATAI, d->wrk.xHat_A1, d->wrk.xHat_A2,
|
||||
d->wrk.xHat_P1, d->wrk.xHat_P2, d->wrk.trI_y, d->wrk.trI_v, d->wrk.dR_z);
|
||||
memcpy (&x[bimp[next]], d->exec.xHout, limp[next] * sizeof (double));
|
||||
memset (&d->exec.unfixed[bimp[next]], 0, limp[next] * sizeof (int));
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (&x[bimp[next]], &d->exec.savex[bimp[next]], limp[next] * sizeof (double));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SNBA::xsnba (SNBA *d)
|
||||
{
|
||||
if (d->run)
|
||||
{
|
||||
int i;
|
||||
RESAMPLE::xresample (d->inresamp);
|
||||
for (i = 0; i < 2 * d->isize; i += 2)
|
||||
{
|
||||
d->inaccum[d->iainidx] = d->inbuff[i];
|
||||
d->iainidx = (d->iainidx + 1) % d->iasize;
|
||||
}
|
||||
d->nsamps += d->isize;
|
||||
while (d->nsamps >= d->incr)
|
||||
{
|
||||
memcpy (&d->xaux[d->xsize - d->incr], &d->inaccum[d->iaoutidx], d->incr * sizeof (double));
|
||||
execFrame (d, d->xaux);
|
||||
d->iaoutidx = (d->iaoutidx + d->incr) % d->iasize;
|
||||
d->nsamps -= d->incr;
|
||||
memcpy (&d->outaccum[d->oainidx], d->xaux, d->incr * sizeof (double));
|
||||
d->oainidx = (d->oainidx + d->incr) % d->oasize;
|
||||
memmove (d->xbase, &d->xbase[d->incr], (2 * d->xsize - d->incr) * sizeof (double));
|
||||
}
|
||||
for (i = 0; i < d->isize; i++)
|
||||
{
|
||||
d->outbuff[2 * i + 0] = d->outaccum[d->oaoutidx];
|
||||
d->outbuff[2 * i + 1] = 0.0;
|
||||
d->oaoutidx = (d->oaoutidx + 1) % d->oasize;
|
||||
}
|
||||
RESAMPLE::xresample (d->outresamp);
|
||||
}
|
||||
else if (d->out != d->in)
|
||||
memcpy (d->out, d->in, d->bsize * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SNBA::SetSNBARun (RXA& rxa, int run)
|
||||
{
|
||||
SNBA *a = rxa.snba.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
BPSNBA::bpsnbaCheck (rxa, rxa.mode, rxa.ndb.p->master_run);
|
||||
RXA::bp1Check (rxa, rxa.amd.p->run, run, rxa.emnr.p->run,
|
||||
rxa.anf.p->run, rxa.anr.p->run);
|
||||
rxa.csDSP.lock();
|
||||
a->run = run;
|
||||
RXA::bp1Set (rxa);
|
||||
BPSNBA::bpsnbaSet (rxa);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void SNBA::SetSNBAovrlp (RXA& rxa, int ovrlp)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
decalc_snba (rxa.snba.p);
|
||||
rxa.snba.p->ovrlp = ovrlp;
|
||||
calc_snba (rxa.snba.p);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SetSNBAasize (RXA& rxa, int size)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->exec.asize = size;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBAnpasses (RXA& rxa, int npasses)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->exec.npasses = npasses;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBAk1 (RXA& rxa, double k1)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->sdet.k1 = k1;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBAk2 (RXA& rxa, double k2)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->sdet.k2 = k2;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBAbridge (RXA& rxa, int bridge)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->sdet.b = bridge;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBApresamps (RXA& rxa, int presamps)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->sdet.pre = presamps;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBApostsamps (RXA& rxa, int postsamps)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->sdet.post = postsamps;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBApmultmin (RXA& rxa, double pmultmin)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.snba.p->scan.pmultmin = pmultmin;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SNBA::SetSNBAOutputBandwidth (RXA& rxa, double flow, double fhigh)
|
||||
{
|
||||
SNBA *a;
|
||||
RESAMPLE *d;
|
||||
double f_low, f_high;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.snba.p;
|
||||
d = a->outresamp;
|
||||
|
||||
if (flow >= 0 && fhigh >= 0)
|
||||
{
|
||||
if (fhigh < a->out_low_cut) fhigh = a->out_low_cut;
|
||||
if (flow > a->out_high_cut) flow = a->out_high_cut;
|
||||
f_low = std::max ( a->out_low_cut, flow);
|
||||
f_high = std::min (a->out_high_cut, fhigh);
|
||||
}
|
||||
else if (flow <= 0 && fhigh <= 0)
|
||||
{
|
||||
if (flow > -a->out_low_cut) flow = -a->out_low_cut;
|
||||
if (fhigh < -a->out_high_cut) fhigh = -a->out_high_cut;
|
||||
f_low = std::max ( a->out_low_cut, -fhigh);
|
||||
f_high = std::min (a->out_high_cut, -flow);
|
||||
}
|
||||
else if (flow < 0 && fhigh > 0)
|
||||
{
|
||||
double absmax = std::max (-flow, fhigh);
|
||||
if (absmax < a->out_low_cut) absmax = a->out_low_cut;
|
||||
f_low = a->out_low_cut;
|
||||
f_high = std::min (a->out_high_cut, absmax);
|
||||
}
|
||||
|
||||
RESAMPLE::setBandwidth_resample (d, f_low, f_high);
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* BPSNBA Bandpass Filter *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// This is a thin wrapper for a notched-bandpass filter (nbp). The basic difference is that it provides
|
||||
// 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)
|
||||
{
|
||||
a->buff = new double[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex));
|
||||
a->bpsnba = NBP::create_nbp (
|
||||
1, // run, always runs (use bpsnba 'run')
|
||||
a->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
|
||||
}
|
||||
|
||||
BPSNBA* BPSNBA::create_bpsnba (
|
||||
int run,
|
||||
int run_notches,
|
||||
int position,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* 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 *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;
|
||||
}
|
||||
|
||||
void BPSNBA::decalc_bpsnba (BPSNBA *a)
|
||||
{
|
||||
NBP::destroy_nbp (a->bpsnba);
|
||||
delete[] (a->buff);
|
||||
}
|
||||
|
||||
void BPSNBA::destroy_bpsnba (BPSNBA *a)
|
||||
{
|
||||
decalc_bpsnba (a);
|
||||
delete[] (a);
|
||||
}
|
||||
|
||||
void BPSNBA::flush_bpsnba (BPSNBA *a)
|
||||
{
|
||||
memset (a->buff, 0, a->size * sizeof (dcomplex));
|
||||
NBP::flush_nbp (a->bpsnba);
|
||||
}
|
||||
|
||||
void BPSNBA::setBuffers_bpsnba (BPSNBA *a, double* in, double* out)
|
||||
{
|
||||
decalc_bpsnba (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_bpsnba (a);
|
||||
}
|
||||
|
||||
void BPSNBA::setSamplerate_bpsnba (BPSNBA *a, int rate)
|
||||
{
|
||||
decalc_bpsnba (a);
|
||||
a->rate = rate;
|
||||
calc_bpsnba (a);
|
||||
}
|
||||
|
||||
void BPSNBA::setSize_bpsnba (BPSNBA *a, int size)
|
||||
{
|
||||
decalc_bpsnba (a);
|
||||
a->size = size;
|
||||
calc_bpsnba (a);
|
||||
}
|
||||
|
||||
void BPSNBA::xbpsnbain (BPSNBA *a, int position)
|
||||
{
|
||||
if (a->run && a->position == position)
|
||||
memcpy (a->buff, a->in, a->size * sizeof (dcomplex));
|
||||
}
|
||||
|
||||
void BPSNBA::xbpsnbaout (BPSNBA *a, int position)
|
||||
{
|
||||
if (a->run && a->position == position)
|
||||
NBP::xnbp (a->bpsnba, 0);
|
||||
}
|
||||
|
||||
void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, 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);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
void BPSNBA::BPSNBASetNC (RXA& rxa, int nc)
|
||||
{
|
||||
BPSNBA *a;
|
||||
rxa.csDSP.lock();
|
||||
a = rxa.bpsnba.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
a->bpsnba->nc = a->nc;
|
||||
NBP::setNc_nbp (a->bpsnba);
|
||||
}
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
|
||||
void BPSNBA::BPSNBASetMP (RXA& rxa, int mp)
|
||||
{
|
||||
BPSNBA *a;
|
||||
a = rxa.bpsnba.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
a->bpsnba->mp = a->mp;
|
||||
NBP::setMp_nbp (a->bpsnba);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
261
wdsp/snb.hpp
Normal file
261
wdsp/snb.hpp
Normal file
@ -0,0 +1,261 @@
|
||||
/* snb.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_snba_h
|
||||
#define wdsp_snba_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class RESAMPLE;
|
||||
class RXA;
|
||||
|
||||
class WDSP_API SNBA
|
||||
{
|
||||
public:
|
||||
int run;
|
||||
double* in;
|
||||
double* out;
|
||||
int inrate;
|
||||
int internalrate;
|
||||
int bsize;
|
||||
int xsize;
|
||||
int ovrlp;
|
||||
int incr;
|
||||
int iasize;
|
||||
int iainidx;
|
||||
int iaoutidx;
|
||||
double* inaccum;
|
||||
double* xbase;
|
||||
double* xaux;
|
||||
int nsamps;
|
||||
int oasize;
|
||||
int oainidx;
|
||||
int oaoutidx;
|
||||
int init_oaoutidx;
|
||||
double* outaccum;
|
||||
|
||||
int resamprun;
|
||||
int isize;
|
||||
RESAMPLE *inresamp;
|
||||
RESAMPLE *outresamp;
|
||||
double* inbuff;
|
||||
double* outbuff;
|
||||
struct _exec
|
||||
{
|
||||
int asize;
|
||||
double* a;
|
||||
double* v;
|
||||
int* detout;
|
||||
double* savex;
|
||||
double* xHout;
|
||||
int* unfixed;
|
||||
int npasses;
|
||||
} exec;
|
||||
struct _det
|
||||
{
|
||||
double k1;
|
||||
double k2;
|
||||
int b;
|
||||
int pre;
|
||||
int post;
|
||||
double* vp;
|
||||
double* vpwr;
|
||||
} sdet;
|
||||
struct _scan
|
||||
{
|
||||
double pmultmin;
|
||||
} scan;
|
||||
struct _wrk
|
||||
{
|
||||
int xHat_a1rows_max;
|
||||
int xHat_a2cols_max;
|
||||
double* xHat_r;
|
||||
double* xHat_ATAI;
|
||||
double* xHat_A1;
|
||||
double* xHat_A2;
|
||||
double* xHat_P1;
|
||||
double* xHat_P2;
|
||||
double* trI_y;
|
||||
double* trI_v;
|
||||
double* dR_z;
|
||||
double* asolve_r;
|
||||
double* asolve_z;
|
||||
} wrk;
|
||||
double out_low_cut;
|
||||
double out_high_cut;
|
||||
|
||||
static SNBA* create_snba (
|
||||
int run,
|
||||
double* in,
|
||||
double* out,
|
||||
int inrate,
|
||||
int internalrate,
|
||||
int bsize,
|
||||
int ovrlp,
|
||||
int xsize,
|
||||
int asize,
|
||||
int npasses,
|
||||
double k1,
|
||||
double k2,
|
||||
int b,
|
||||
int pre,
|
||||
int post,
|
||||
double pmultmin,
|
||||
double out_low_cut,
|
||||
double out_high_cut
|
||||
);
|
||||
static void destroy_snba (SNBA *d);
|
||||
static void flush_snba (SNBA *d);
|
||||
static void xsnba (SNBA *d);
|
||||
static void setBuffers_snba (SNBA *a, double* in, double* out);
|
||||
static void setSamplerate_snba (SNBA *a, int rate);
|
||||
static void setSize_snba (SNBA *a, int size);
|
||||
// RXA
|
||||
static void SetSNBAOutputBandwidth (RXA& rxa, double flow, double fhigh);
|
||||
static void SetSNBARun (RXA& rxa, int run);
|
||||
static void SetSNBAovrlp (RXA& rxa, int ovrlp);
|
||||
static void SetSNBAasize (RXA& rxa, int size);
|
||||
static void SetSNBAnpasses (RXA& rxa, int npasses);
|
||||
static void SetSNBAk1 (RXA& rxa, double k1);
|
||||
static void SetSNBAk2 (RXA& rxa, double k2);
|
||||
static void SetSNBAbridge (RXA& rxa, int bridge);
|
||||
static void SetSNBApresamps (RXA& rxa, int presamps);
|
||||
static void SetSNBApostsamps (RXA& rxa, int postsamps);
|
||||
static void SetSNBApmultmin (RXA& rxa, double pmultmin);
|
||||
|
||||
private:
|
||||
static void calc_snba (SNBA *d);
|
||||
static void decalc_snba (SNBA *d);
|
||||
static void ATAc0 (int n, int nr, double* A, double* r);
|
||||
static void multA1TA2(double* a1, double* a2, int m, int n, int q, double* c);
|
||||
static void multXKE(double* a, double* xk, int m, int q, int p, double* vout);
|
||||
static void multAv(double* a, double* v, int m, int q, double* vout);
|
||||
static void xHat(
|
||||
int xusize,
|
||||
int asize,
|
||||
double* xk,
|
||||
double* a,
|
||||
double* xout,
|
||||
double* r,
|
||||
double* ATAI,
|
||||
double* A1,
|
||||
double* A2,
|
||||
double* P1,
|
||||
double* P2,
|
||||
double* trI_y,
|
||||
double* trI_v,
|
||||
double* dR_z
|
||||
);
|
||||
static void invf(int xsize, int asize, double* a, double* x, double* v);
|
||||
static void det(SNBA *d, int asize, double* v, int* detout);
|
||||
static int scanFrame(
|
||||
int xsize,
|
||||
int pval,
|
||||
double pmultmin,
|
||||
int* det,
|
||||
int* bimp,
|
||||
int* limp,
|
||||
int* befimp,
|
||||
int* aftimp,
|
||||
int* p_opt,
|
||||
int* next
|
||||
);
|
||||
static void execFrame(SNBA *d, double* x);
|
||||
};
|
||||
|
||||
|
||||
class NBP;
|
||||
class NOTCHDB;
|
||||
|
||||
class WDSP_API BPSNBA
|
||||
{
|
||||
public:
|
||||
int run; // run the filter
|
||||
int run_notches; // use the notches, vs straight bandpass
|
||||
int position; // position in the processing pipeline
|
||||
int size; // buffer size
|
||||
int nc; // number of filter coefficients
|
||||
int mp; // minimum phase flag
|
||||
double* in; // input buffer
|
||||
double* out; // output buffer
|
||||
int rate; // sample rate
|
||||
double* 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
|
||||
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
|
||||
|
||||
static BPSNBA* create_bpsnba (
|
||||
int run,
|
||||
int run_notches,
|
||||
int position,
|
||||
int size,
|
||||
int nc,
|
||||
int mp,
|
||||
double* in,
|
||||
double* 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
|
||||
);
|
||||
static void destroy_bpsnba (BPSNBA *a);
|
||||
static void flush_bpsnba (BPSNBA *a);
|
||||
static void setBuffers_bpsnba (BPSNBA *a, double* in, double* 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
|
||||
static void BPSNBASetNC (RXA& rxa, int nc);
|
||||
static void BPSNBASetMP (RXA& rxa, int mp);
|
||||
static void bpsnbaCheck (RXA& rxa, int mode, int notch_run);
|
||||
static void bpsnbaSet (RXA& rxa);
|
||||
|
||||
private:
|
||||
static void calc_bpsnba (BPSNBA *a);
|
||||
static void decalc_bpsnba (BPSNBA *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
389
wdsp/ssql.cpp
Normal file
389
wdsp/ssql.cpp
Normal file
@ -0,0 +1,389 @@
|
||||
/* ssql.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 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@pratt.one
|
||||
|
||||
*/
|
||||
|
||||
#include "comm.hpp"
|
||||
#include "cblock.hpp"
|
||||
#include "ssql.hpp"
|
||||
#include "iir.hpp"
|
||||
#include "RXA.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Frequency to Voltage Converter *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
FTOV* FTOV::create_ftov (int run, int size, int rate, int rsize, double fmax, double* in, double* out)
|
||||
{
|
||||
FTOV *a = new FTOV;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->rate = rate;
|
||||
a->rsize = rsize;
|
||||
a->fmax = fmax;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->eps = 0.01;
|
||||
a->ring = new int[a->rsize]; // (int*) malloc0 (a->rsize * sizeof (int));
|
||||
a->rptr = 0;
|
||||
a->inlast = 0.0;
|
||||
a->rcount = 0;
|
||||
a->div = a->fmax * 2.0 * a->rsize / a->rate; // fmax * 2 = zero-crossings/sec
|
||||
// rsize / rate = sec of data in ring
|
||||
// product is # zero-crossings in ring at fmax
|
||||
return a;
|
||||
}
|
||||
|
||||
void FTOV::destroy_ftov (FTOV *a)
|
||||
{
|
||||
delete[] (a->ring);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void FTOV::flush_ftov (FTOV *a)
|
||||
{
|
||||
memset (a->ring, 0, a->rsize * sizeof (int));
|
||||
a->rptr = 0;
|
||||
a->rcount = 0;
|
||||
a->inlast = 0.0;
|
||||
}
|
||||
|
||||
void FTOV::xftov (FTOV *a)
|
||||
{
|
||||
// 'ftov' does frequency to voltage conversion looking only at zero crossings of an
|
||||
// AC (DC blocked) signal, i.e., ignoring signal amplitude.
|
||||
if (a->run)
|
||||
{
|
||||
if (a->ring[a->rptr] == 1) // if current ring location is a '1' ...
|
||||
{
|
||||
a->rcount--; // decrement the count
|
||||
a->ring[a->rptr] = 0; // set the location to '0'
|
||||
}
|
||||
if ((a->inlast * a->in[0] < 0.0) && // different signs mean zero-crossing
|
||||
(fabs (a->inlast - a->in[0]) > a->eps))
|
||||
{
|
||||
a->ring[a->rptr] = 1; // set the ring location to '1'
|
||||
a->rcount++; // increment the count
|
||||
}
|
||||
if (++a->rptr == a->rsize) a->rptr = 0; // increment and wrap the pointer as needed
|
||||
a->out[0] = std::min (1.0, (double)a->rcount / a->div); // calculate the output sample
|
||||
a->inlast = a->in[a->size - 1]; // save the last input sample for next buffer
|
||||
for (int i = 1; i < a->size; i++)
|
||||
{
|
||||
if (a->ring[a->rptr] == 1) // if current ring location is '1' ...
|
||||
{
|
||||
a->rcount--; // decrement the count
|
||||
a->ring[a->rptr] = 0; // set the location to '0'
|
||||
}
|
||||
if ((a->in[i - 1] * a->in[i] < 0.0) && // different signs mean zero-crossing
|
||||
(fabs (a->in[i - 1] - a->in[i]) > a->eps))
|
||||
{
|
||||
a->ring[a->rptr] = 1; // set the ring location to '1'
|
||||
a->rcount++; // increment the count
|
||||
}
|
||||
if (++a->rptr == a->rsize) a->rptr = 0; // increment and wrap the pointer as needed
|
||||
a->out[i] = std::min(1.0, (double)a->rcount / a->div); // calculate the output sample
|
||||
}
|
||||
}
|
||||
}
|
||||
/*******************************************************************************************************/
|
||||
/********************************** END Frequency to Voltage Converter *********************************/
|
||||
|
||||
|
||||
|
||||
void SSQL::compute_ssql_slews(SSQL *a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
delta = PI / (double)a->ntup;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntup; i++)
|
||||
{
|
||||
a->cup[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 - cos(theta));
|
||||
theta += delta;
|
||||
}
|
||||
delta = PI / (double)a->ntdown;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ntdown; i++)
|
||||
{
|
||||
a->cdown[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 + cos(theta));
|
||||
theta += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void SSQL::calc_ssql (SSQL *a)
|
||||
{
|
||||
a->b1 = new double[a->size * 2]; // (double*) malloc0 (a->size * sizeof (complex));
|
||||
a->dcbl = CBL::create_cbl (1, a->size, a->in, a->b1, 0, a->rate, 0.02);
|
||||
a->ibuff = new double[a->size]; // (double*) malloc0 (a->size * sizeof (double));
|
||||
a->ftovbuff = new double[a->size]; // (double*) malloc0(a->size * sizeof (double));
|
||||
a->cvtr = FTOV::create_ftov (1, a->size, a->rate, a->ftov_rsize, a->ftov_fmax, a->ibuff, a->ftovbuff);
|
||||
a->lpbuff = new double[a->size]; // (double*) malloc0 (a->size * sizeof (double));
|
||||
a->filt = DBQLP::create_dbqlp (1, a->size, a->ftovbuff, a->lpbuff, a->rate, 11.3, 1.0, 1.0, 1);
|
||||
a->wdbuff = new int[a->size]; // (int*) malloc0 (a->size * sizeof (int));
|
||||
a->tr_signal = new int[a->size]; // (int*) malloc0 (a->size * sizeof (int));
|
||||
// window detector
|
||||
a->wdmult = exp (-1.0 / (a->rate * a->wdtau));
|
||||
a->wdaverage = 0.0;
|
||||
// trigger
|
||||
a->tr_voltage = a->tr_thresh;
|
||||
a->mute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_mute));
|
||||
a->unmute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_unmute));
|
||||
// level change
|
||||
a->ntup = (int)(a->tup * a->rate);
|
||||
a->ntdown = (int)(a->tdown * a->rate);
|
||||
a->cup = new double[a->ntup + 1]; // (double*) malloc0 ((a->ntup + 1) * sizeof (double));
|
||||
a->cdown = new double[a->ntdown + 1]; // (double*) malloc0 ((a->ntdown + 1) * sizeof (double));
|
||||
compute_ssql_slews (a);
|
||||
// control
|
||||
a->state = 0;
|
||||
a->count = 0;
|
||||
}
|
||||
|
||||
void SSQL::decalc_ssql (SSQL *a)
|
||||
{
|
||||
delete[] (a->tr_signal);
|
||||
delete[] (a->wdbuff);
|
||||
DBQLP::destroy_dbqlp (a->filt);
|
||||
delete[] (a->lpbuff);
|
||||
FTOV::destroy_ftov (a->cvtr);
|
||||
delete[] (a->ftovbuff);
|
||||
delete[] (a->ibuff);
|
||||
CBL::destroy_cbl (a->dcbl);
|
||||
delete[] (a->b1);
|
||||
delete[] (a->cdown);
|
||||
delete[] (a->cup);
|
||||
}
|
||||
|
||||
SSQL* SSQL::create_ssql (
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int rate,
|
||||
double tup,
|
||||
double tdown,
|
||||
double muted_gain,
|
||||
double tau_mute,
|
||||
double tau_unmute,
|
||||
double wthresh,
|
||||
double tr_thresh,
|
||||
int rsize,
|
||||
double fmax
|
||||
)
|
||||
{
|
||||
SSQL *a = new SSQL;
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = rate;
|
||||
a->tup = tup;
|
||||
a->tdown = tdown;
|
||||
a->muted_gain = muted_gain;
|
||||
a->tr_tau_mute = tau_mute;
|
||||
a->tr_tau_unmute = tau_unmute;
|
||||
a->wthresh = wthresh; // PRIMARY SQUELCH THRESHOLD CONTROL
|
||||
a->tr_thresh = tr_thresh; // value between tr_ss_unmute and tr_ss_mute, default = 0.8197
|
||||
a->tr_ss_mute = 1.0;
|
||||
a->tr_ss_unmute = 0.3125;
|
||||
a->wdtau = 0.5;
|
||||
a->ftov_rsize = rsize;
|
||||
a->ftov_fmax = fmax;
|
||||
calc_ssql (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void SSQL::destroy_ssql (SSQL *a)
|
||||
{
|
||||
decalc_ssql (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void SSQL::flush_ssql (SSQL *a)
|
||||
{
|
||||
|
||||
memset (a->b1, 0, a->size * sizeof (dcomplex));
|
||||
CBL::flush_cbl (a->dcbl);
|
||||
memset (a->ibuff, 0, a->size * sizeof (double));
|
||||
memset (a->ftovbuff, 0, a->size * sizeof (double));
|
||||
FTOV::flush_ftov (a->cvtr);
|
||||
memset (a->lpbuff, 0, a->size * sizeof (double));
|
||||
DBQLP::flush_dbqlp (a->filt);
|
||||
memset (a->wdbuff, 0, a->size * sizeof (int));
|
||||
memset (a->tr_signal, 0, a->size * sizeof (int));
|
||||
}
|
||||
|
||||
enum _ssqlstate
|
||||
{
|
||||
MUTED,
|
||||
INCREASE,
|
||||
UNMUTED,
|
||||
DECREASE
|
||||
};
|
||||
|
||||
void SSQL::xssql (SSQL *a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
CBL::xcbl (a->dcbl); // dc block the input signal
|
||||
for (int i = 0; i < a->size; i++) // extract 'I' component
|
||||
a->ibuff[i] = a->b1[2 * i];
|
||||
FTOV::xftov (a->cvtr); // convert frequency to voltage, ignoring amplitude
|
||||
// WriteAudioWDSP(20.0, a->rate, a->size, a->ftovbuff, 4, 0.99);
|
||||
DBQLP::xdbqlp (a->filt); // low-pass filter
|
||||
// WriteAudioWDSP(20.0, a->rate, a->size, a->lpbuff, 4, 0.99);
|
||||
// calculate the output of the window detector for each sample
|
||||
for (int i = 0; i < a->size; i++)
|
||||
{
|
||||
a->wdaverage = a->wdmult * a->wdaverage + (1.0 - a->wdmult) * a->lpbuff[i];
|
||||
if ((a->lpbuff[i] - a->wdaverage) > a->wthresh || (a->wdaverage - a->lpbuff[i]) > a->wthresh)
|
||||
a->wdbuff[i] = 0; // signal unmute
|
||||
else
|
||||
a->wdbuff[i] = 1; // signal mute
|
||||
}
|
||||
// calculate the trigger signal for each sample
|
||||
for (int i = 0; i < a->size; i++)
|
||||
{
|
||||
if (a->wdbuff[i] == 0)
|
||||
a->tr_voltage += (a->tr_ss_unmute - a->tr_voltage) * a->unmute_mult;
|
||||
if (a->wdbuff[i] == 1)
|
||||
a->tr_voltage += (a->tr_ss_mute - a->tr_voltage) * a->mute_mult;
|
||||
if (a->tr_voltage > a->tr_thresh) a->tr_signal[i] = 0; // muted
|
||||
else a->tr_signal[i] = 1; // unmuted
|
||||
}
|
||||
// execute state machine; calculate audio output
|
||||
for (int i = 0; i < a->size; i++)
|
||||
{
|
||||
switch (a->state)
|
||||
{
|
||||
case MUTED:
|
||||
if (a->tr_signal[i] == 1)
|
||||
{
|
||||
a->state = INCREASE;
|
||||
a->count = a->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];
|
||||
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];
|
||||
if (a->count-- == 0)
|
||||
a->state = UNMUTED;
|
||||
break;
|
||||
case UNMUTED:
|
||||
if (a->tr_signal[i] == 0)
|
||||
{
|
||||
a->state = DECREASE;
|
||||
a->count = a->ntdown;
|
||||
}
|
||||
a->out[2 * i + 0] = a->in[2 * i + 0];
|
||||
a->out[2 * i + 1] = a->in[2 * i + 1];
|
||||
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];
|
||||
if (a->count-- == 0)
|
||||
a->state = MUTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof(dcomplex));
|
||||
}
|
||||
|
||||
void SSQL::setBuffers_ssql (SSQL *a, double* in, double* out)
|
||||
{
|
||||
decalc_ssql (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_ssql (a);
|
||||
}
|
||||
|
||||
void SSQL::setSamplerate_ssql (SSQL *a, int rate)
|
||||
{
|
||||
decalc_ssql (a);
|
||||
a->rate = rate;
|
||||
calc_ssql (a);
|
||||
}
|
||||
|
||||
void SSQL::setSize_ssql (SSQL *a, int size)
|
||||
{
|
||||
decalc_ssql (a);
|
||||
a->size = size;
|
||||
calc_ssql (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SSQL::SetSSQLRun (RXA& rxa, int run)
|
||||
{
|
||||
rxa.csDSP.lock();
|
||||
rxa.ssql.p->run = run;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SSQL::SetSSQLThreshold (RXA& rxa, double threshold)
|
||||
{
|
||||
// 'threshold' should be between 0.0 and 1.0
|
||||
// WU2O testing: 0.16 is a good default for 'threshold'; => 0.08 for 'wthresh'
|
||||
rxa.csDSP.lock();
|
||||
rxa.ssql.p->wthresh = threshold / 2.0;
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SSQL::SetSSQLTauMute (RXA& rxa, double tau_mute)
|
||||
{
|
||||
// reasonable (wide) range is 0.1 to 2.0
|
||||
// WU2O testing: 0.1 is good default value
|
||||
SSQL *a = rxa.ssql.p;
|
||||
rxa.csDSP.lock();
|
||||
a->tr_tau_mute = tau_mute;
|
||||
a->mute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_mute));
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
void SSQL::SetSSQLTauUnMute (RXA& rxa, double tau_unmute)
|
||||
{
|
||||
// reasonable (wide) range is 0.1 to 1.0
|
||||
// WU2O testing: 0.1 is good default value
|
||||
SSQL *a = rxa.ssql.p;
|
||||
rxa.csDSP.lock();
|
||||
a->tr_tau_unmute = tau_unmute;
|
||||
a->unmute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_unmute));
|
||||
rxa.csDSP.unlock();
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
143
wdsp/ssql.hpp
Normal file
143
wdsp/ssql.hpp
Normal file
@ -0,0 +1,143 @@
|
||||
/* ssql.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 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@pratt.one
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_ssql_h
|
||||
#define wdsp_ssql_h
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API FTOV // Frequency to Voltage Converter
|
||||
{
|
||||
public:
|
||||
int run; // 0 => don't run; 1 => run
|
||||
int size; // buffer size
|
||||
int rate; // sample-rate
|
||||
int rsize; // rate * time_to_fill_ring, e.g., 48K/s * 50ms = 2400
|
||||
double fmax; // frequency (Hz) for full output, e.g., 2000 (Hz)
|
||||
double* in; // pointer to the intput buffer for ftov
|
||||
double* out; // pointer to the output buffer for ftov
|
||||
int* ring; // pointer to the base of the ring
|
||||
int rptr; // index into the ring
|
||||
double inlast; // holds last sample from previous buffer
|
||||
int rcount; // count of zero-crossings currently in the ring
|
||||
double div; // divisor for 'rcount' to produce output of 1.0 at 'fmax'
|
||||
double eps; // minimum input change to count as a signal edge transition
|
||||
|
||||
static FTOV* create_ftov (int run, int size, int rate, int rsize, double fmax, double* in, double* out);
|
||||
static void destroy_ftov (FTOV *a);
|
||||
static void flush_ftov (FTOV *a);
|
||||
static void xftov (FTOV *a);
|
||||
};
|
||||
|
||||
class CBL;
|
||||
class FTDV;
|
||||
class BQLP;
|
||||
class RXA;
|
||||
|
||||
class WDSP_API SSQL // Syllabic Squelch
|
||||
{
|
||||
public:
|
||||
int run; // 0 if squelch system is OFF; 1 if it's ON
|
||||
int size; // size of input/output buffers
|
||||
double* in; // squelch input signal buffer
|
||||
double* out; // squelch output signal buffer
|
||||
int rate; // sample rate
|
||||
int state; // state machine control
|
||||
int count; // count variable for raised cosine transitions
|
||||
double tup; // time for turn-on transition
|
||||
double tdown; // time for turn-off transition
|
||||
int ntup; // number of samples for turn-on transition
|
||||
int ntdown; // number of samples for turn-off transition
|
||||
double* cup; // coefficients for up-slew
|
||||
double* cdown; // coefficients for down-slew
|
||||
double muted_gain; // audio gain while muted; 0.0 for complete silence
|
||||
|
||||
double* b1; // buffer to hold output of dc-block function
|
||||
double* ibuff; // buffer containing only 'I' component
|
||||
double* ftovbuff; // buffer containing output of f to v converter
|
||||
double* lpbuff; // buffer containing output of low-pass filter
|
||||
int* wdbuff; // buffer containing output of window detector
|
||||
CBL *dcbl; // pointer to DC Blocker data structure
|
||||
FTOV *cvtr; // pointer to F to V Converter data structure
|
||||
BQLP *filt; // pointer to Bi-Quad Low-Pass Filter data structure
|
||||
int ftov_rsize; // ring size for f_to_v converter
|
||||
double ftov_fmax; // fmax for f_to_v converter
|
||||
// window detector
|
||||
double wdtau; // window detector time constant
|
||||
double wdmult; // window detector time constant multiplier
|
||||
double wdaverage; // average signal value
|
||||
double wthresh; // window threshold above/below average
|
||||
// trigger
|
||||
double tr_thresh; // trigger threshold: 100K/(100K+22K)=0.8197
|
||||
double tr_tau_unmute; // trigger unmute time-constant: (100K||220K)*10uF = 0.6875
|
||||
double tr_ss_unmute; // trigger steady-state level for unmute: 100K/(100K+220K)=0.3125
|
||||
double tr_tau_mute; // trigger mute time-constant: 220K*10uF = 2.2
|
||||
double tr_ss_mute; // trigger steady-state level for mute: 1.0
|
||||
double tr_voltage; // trigger voltage
|
||||
double mute_mult; // multiplier for successive voltage calcs when muted
|
||||
double unmute_mult; // multiplier for successive voltage calcs when unmuted
|
||||
int* tr_signal; // trigger signal, 0 or 1
|
||||
|
||||
static SSQL* create_ssql (
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int rate,
|
||||
double tup,
|
||||
double tdown,
|
||||
double muted_gain,
|
||||
double tau_mute,
|
||||
double tau_unmute,
|
||||
double wthresh,
|
||||
double tr_thresh,
|
||||
int rsize,
|
||||
double fmax
|
||||
);
|
||||
static void destroy_ssql (SSQL *a);
|
||||
static void flush_ssql (SSQL *a);
|
||||
static void xssql (SSQL *a);
|
||||
static void setBuffers_ssql (SSQL *a, double* in, double* out);
|
||||
static void setSamplerate_ssql (SSQL *a, int rate);
|
||||
static void setSize_ssql (SSQL *a, int size);
|
||||
// RXA Properties
|
||||
static void SetSSQLRun (RXA& rxa, int run);
|
||||
static void SetSSQLThreshold (RXA& rxa, double threshold);
|
||||
static void SetSSQLTauMute (RXA& rxa, double tau_mute);
|
||||
static void SetSSQLTauUnMute (RXA& rxa, double tau_unmute);
|
||||
|
||||
private:
|
||||
static void compute_ssql_slews(SSQL *a);
|
||||
static void calc_ssql (SSQL *a);
|
||||
static void decalc_ssql (SSQL *a);
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
55
wdsp/unit.hpp
Normal file
55
wdsp/unit.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
/* unit.hpp
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef wdsp_unit_h
|
||||
#define wdsp_unit_h
|
||||
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
class WDSP_API Unit
|
||||
{
|
||||
public:
|
||||
int in_rate; // input samplerate
|
||||
int out_rate; // output samplerate
|
||||
int in_size; // input buffsize (complex samples) in a fexchange() operation
|
||||
int dsp_rate; // sample rate for mainstream dsp processing
|
||||
int dsp_size; // number complex samples processed per buffer in mainstream dsp processing
|
||||
int dsp_insize; // size (complex samples) of the output of the r1 (input) buffer
|
||||
int dsp_outsize; // size (complex samples) of the input of the r2 (output) buffer
|
||||
int out_size; // output buffsize (complex samples) in a fexchange() operation
|
||||
QRecursiveMutex csDSP; // used to block dsp while parameters are updated or buffers flushed
|
||||
QRecursiveMutex csEXCH; // used to block fexchange() while parameters are updated or buffers flushed
|
||||
int state; // 0 for unit OFF; 1 for unit ON
|
||||
};
|
||||
|
||||
} // namespace WDSP
|
||||
|
||||
#endif
|
||||
266
wdsp/varsamp.cpp
Normal file
266
wdsp/varsamp.cpp
Normal file
@ -0,0 +1,266 @@
|
||||
/* varsamp.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 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 "fir.hpp"
|
||||
#include "varsamp.hpp"
|
||||
|
||||
namespace WDSP {
|
||||
|
||||
void VARSAMP::calc_varsamp (VARSAMP *a)
|
||||
{
|
||||
double min_rate, max_rate, norm_rate;
|
||||
double fc_norm_high, fc_norm_low;
|
||||
a->nom_ratio = (double)a->out_rate / (double)a->in_rate;
|
||||
a->cvar = a->var * a->nom_ratio;
|
||||
a->inv_cvar = 1.0 / a->cvar;
|
||||
a->old_inv_cvar = a->inv_cvar;
|
||||
a->dicvar = 0.0;
|
||||
a->delta = fabs (1.0 / a->cvar - 1.0);
|
||||
a->fc = a->fcin;
|
||||
if (a->out_rate >= a->in_rate)
|
||||
{
|
||||
min_rate = (double)a->in_rate;
|
||||
max_rate = (double)a->out_rate;
|
||||
norm_rate = min_rate;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_rate = (double)a->out_rate;
|
||||
max_rate = (double)a->in_rate;
|
||||
norm_rate = max_rate;
|
||||
}
|
||||
if (a->fc == 0.0) a->fc = 0.95 * 0.45 * min_rate;
|
||||
fc_norm_high = a->fc / norm_rate;
|
||||
if (a->fc_low < 0.0)
|
||||
fc_norm_low = - fc_norm_high;
|
||||
else
|
||||
fc_norm_low = a->fc_low / norm_rate;
|
||||
a->rsize = (int)(140.0 * norm_rate / min_rate);
|
||||
a->ncoef = a->rsize + 1;
|
||||
a->ncoef += (a->R - 1) * (a->ncoef - 1);
|
||||
a->h = FIR::fir_bandpass(a->ncoef, fc_norm_low, fc_norm_high, (double)a->R, 1, 0, (double)a->R * a->gain);
|
||||
// print_impulse ("imp.txt", a->ncoef, a->h, 0, 0);
|
||||
a->ring = new double[a->rsize * 2]; // (double *)malloc0(a->rsize * sizeof(complex));
|
||||
a->idx_in = a->rsize - 1;
|
||||
a->h_offset = 0.0;
|
||||
a->hs = new double[a->rsize]; // (double *)malloc0 (a->rsize * sizeof (double));
|
||||
a->isamps = 0.0;
|
||||
}
|
||||
|
||||
void VARSAMP::decalc_varsamp (VARSAMP *a)
|
||||
{
|
||||
delete[] (a->hs);
|
||||
delete[] (a->ring);
|
||||
delete[] (a->h);
|
||||
}
|
||||
|
||||
VARSAMP* VARSAMP::create_varsamp (
|
||||
int run,
|
||||
int size,
|
||||
double* in,
|
||||
double* out,
|
||||
int in_rate,
|
||||
int out_rate,
|
||||
double fc,
|
||||
double fc_low,
|
||||
int R,
|
||||
double gain,
|
||||
double var,
|
||||
int varmode
|
||||
)
|
||||
{
|
||||
VARSAMP *a = new VARSAMP;
|
||||
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->in_rate = in_rate;
|
||||
a->out_rate = out_rate;
|
||||
a->fcin = fc;
|
||||
a->fc_low = fc_low;
|
||||
a->R = R;
|
||||
a->gain = gain;
|
||||
a->var = var;
|
||||
a->varmode = varmode;
|
||||
calc_varsamp (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void VARSAMP::destroy_varsamp (VARSAMP *a)
|
||||
{
|
||||
decalc_varsamp (a);
|
||||
delete (a);
|
||||
}
|
||||
|
||||
void VARSAMP::flush_varsamp (VARSAMP *a)
|
||||
{
|
||||
memset (a->ring, 0, a->rsize * sizeof (dcomplex));
|
||||
a->idx_in = a->rsize - 1;
|
||||
a->h_offset = 0.0;
|
||||
a->isamps = 0.0;
|
||||
}
|
||||
|
||||
void VARSAMP::hshift (VARSAMP *a)
|
||||
{
|
||||
int i, j, k;
|
||||
int hidx;
|
||||
double frac, pos;
|
||||
pos = (double)a->R * a->h_offset;
|
||||
hidx = (int)(pos);
|
||||
frac = pos - (double)hidx;
|
||||
for (i = a->rsize - 1, j = hidx, k = hidx + 1; i >= 0; i--, j += a->R, k += a->R)
|
||||
a->hs[i] = a->h[j] + frac * (a->h[k] - a->h[j]);
|
||||
}
|
||||
|
||||
int VARSAMP::xvarsamp (VARSAMP *a, double var)
|
||||
{
|
||||
int outsamps = 0;
|
||||
uint64_t* picvar;
|
||||
uint64_t N;
|
||||
a->var = var;
|
||||
a->old_inv_cvar = a->inv_cvar;
|
||||
a->cvar = a->var * a->nom_ratio;
|
||||
a->inv_cvar = 1.0 / a->cvar;
|
||||
if (a->varmode)
|
||||
{
|
||||
a->dicvar = (a->inv_cvar - a->old_inv_cvar) / (double)a->size;
|
||||
a->inv_cvar = a->old_inv_cvar;
|
||||
}
|
||||
else a->dicvar = 0.0;
|
||||
if (a->run)
|
||||
{
|
||||
int i, j;
|
||||
int idx_out;
|
||||
double I, Q;
|
||||
for (i = 0; i < a->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];
|
||||
a->inv_cvar += a->dicvar;
|
||||
picvar = (uint64_t*)(&a->inv_cvar);
|
||||
N = *picvar & 0xffffffffffff0000;
|
||||
a->inv_cvar = *((double *)&N);
|
||||
a->delta = 1.0 - a->inv_cvar;
|
||||
while (a->isamps < 1.0)
|
||||
{
|
||||
I = 0.0;
|
||||
Q = 0.0;
|
||||
hshift (a);
|
||||
a->h_offset += a->delta;
|
||||
while (a->h_offset >= 1.0) a->h_offset -= 1.0;
|
||||
while (a->h_offset < 0.0) a->h_offset += 1.0;
|
||||
for (j = 0; j < a->rsize; j++)
|
||||
{
|
||||
if ((idx_out = a->idx_in + j) >= a->rsize) idx_out -= a->rsize;
|
||||
I += a->hs[j] * a->ring[2 * idx_out + 0];
|
||||
Q += a->hs[j] * a->ring[2 * idx_out + 1];
|
||||
}
|
||||
a->out[2 * outsamps + 0] = I;
|
||||
a->out[2 * outsamps + 1] = Q;
|
||||
outsamps++;
|
||||
a->isamps += a->inv_cvar;
|
||||
}
|
||||
a->isamps -= 1.0;
|
||||
if (--a->idx_in < 0) a->idx_in = a->rsize - 1;
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (dcomplex));
|
||||
return outsamps;
|
||||
}
|
||||
|
||||
void VARSAMP::setBuffers_varsamp (VARSAMP *a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void VARSAMP::setSize_varsamp (VARSAMP *a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_varsamp (a);
|
||||
}
|
||||
|
||||
void VARSAMP::setInRate_varsamp (VARSAMP *a, int rate)
|
||||
{
|
||||
decalc_varsamp (a);
|
||||
a->in_rate = rate;
|
||||
calc_varsamp (a);
|
||||
}
|
||||
|
||||
void VARSAMP::setOutRate_varsamp (VARSAMP *a, int rate)
|
||||
{
|
||||
decalc_varsamp (a);
|
||||
a->out_rate = rate;
|
||||
calc_varsamp (a);
|
||||
}
|
||||
|
||||
void VARSAMP::setFCLow_varsamp (VARSAMP *a, double fc_low)
|
||||
{
|
||||
if (fc_low != a->fc_low)
|
||||
{
|
||||
decalc_varsamp (a);
|
||||
a->fc_low = fc_low;
|
||||
calc_varsamp (a);
|
||||
}
|
||||
}
|
||||
|
||||
void VARSAMP::setBandwidth_varsamp (VARSAMP *a, double fc_low, double fc_high)
|
||||
{
|
||||
if (fc_low != a->fc_low || fc_high != a->fcin)
|
||||
{
|
||||
decalc_varsamp (a);
|
||||
a->fc_low = fc_low;
|
||||
a->fcin = fc_high;
|
||||
calc_varsamp (a);
|
||||
}
|
||||
}
|
||||
|
||||
// exported calls
|
||||
|
||||
void* VARSAMP::create_varsampV (int in_rate, int out_rate, int R)
|
||||
{
|
||||
return (void *)create_varsamp (1, 0, 0, 0, in_rate, out_rate, 0.0, -1.0, R, 1.0, 1.0, 1);
|
||||
}
|
||||
|
||||
void VARSAMP::xvarsampV (double* input, double* output, int numsamps, double var, int* outsamps, void* ptr)
|
||||
{
|
||||
VARSAMP *a = (VARSAMP*) ptr;
|
||||
a->in = input;
|
||||
a->out = output;
|
||||
a->size = numsamps;
|
||||
*outsamps = xvarsamp(a, var);
|
||||
}
|
||||
|
||||
void VARSAMP::destroy_varsampV (void* ptr)
|
||||
{
|
||||
destroy_varsamp ( (VARSAMP*) ptr );
|
||||
}
|
||||
|
||||
} // namespace WDSP
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user