first commit
This commit is contained in:
commit
89c8a0e2b5
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# Build output
|
||||
obj/
|
||||
lib/
|
||||
|
||||
# Java build output
|
||||
java/build/
|
||||
|
||||
# third_party — only fftw is downloaded manually, rnnoise and libspecbleach are tracked
|
||||
third_party/fftw/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Editor
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
340
COPYING
Normal file
340
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.
|
||||
|
||||
131140
FDnoiseIQ.c
Normal file
131140
FDnoiseIQ.c
Normal file
File diff suppressed because it is too large
Load Diff
8
FDnoiseIQ.h
Normal file
8
FDnoiseIQ.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _FDnoiseIQ_h
|
||||
#define _FDnoiseIQ_h
|
||||
|
||||
extern int FDnoise_frames;
|
||||
extern double FDnoise[];
|
||||
|
||||
#endif
|
||||
|
||||
228
Makefile
Normal file
228
Makefile
Normal file
@ -0,0 +1,228 @@
|
||||
#
|
||||
# Makefile for WDSP library
|
||||
# Builds static, shared and (optionally) Java JNI libraries.
|
||||
#
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
LIBDIR ?= $(PREFIX)/lib
|
||||
INCLUDEDIR ?= $(PREFIX)/include
|
||||
|
||||
CC ?= gcc
|
||||
AR ?= ar
|
||||
RANLIB ?= ranlib
|
||||
|
||||
CFLAGS ?= -pthread -O3 -D_GNU_SOURCE -Wno-parentheses
|
||||
CPPFLAGS ?=
|
||||
LDFLAGS ?=
|
||||
JNI_CFLAGS ?= -std=gnu89 -Wno-implicit-function-declaration -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-incompatible-pointer-types-discards-qualifiers
|
||||
|
||||
FFTWINCLUDE := $(shell pkg-config --cflags fftw3 2>/dev/null)
|
||||
FFTWLIBS := $(shell pkg-config --libs fftw3 2>/dev/null)
|
||||
FFTWLIBS_F := $(shell pkg-config --libs fftw3f 2>/dev/null)
|
||||
|
||||
ifeq ($(strip $(FFTWLIBS)),)
|
||||
FFTWLIBS := -lfftw3
|
||||
endif
|
||||
ifeq ($(strip $(FFTWLIBS_F)),)
|
||||
FFTWLIBS_F := -lfftw3f
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SHARED_EXT := dylib
|
||||
SHARED_LDFLAGS := -dynamiclib
|
||||
PIC_CFLAGS :=
|
||||
NOEXECSTACK :=
|
||||
JAVA_OS_INCLUDE := darwin
|
||||
else
|
||||
SHARED_EXT := so
|
||||
SHARED_LDFLAGS := -shared
|
||||
PIC_CFLAGS := -fPIC
|
||||
NOEXECSTACK := -Wl,-z,noexecstack
|
||||
JAVA_OS_INCLUDE := linux
|
||||
endif
|
||||
|
||||
OBJDIR := obj
|
||||
OUTDIR := lib
|
||||
|
||||
STATIC_LIB_FILE := libwdsp.a
|
||||
SHARED_LIB_FILE := libwdsp.$(SHARED_EXT)
|
||||
JAVA_LIB_FILE := libwdspj.$(SHARED_EXT)
|
||||
|
||||
STATIC_LIB := $(OUTDIR)/$(STATIC_LIB_FILE)
|
||||
SHARED_LIB := $(OUTDIR)/$(SHARED_LIB_FILE)
|
||||
JAVA_LIB := $(OUTDIR)/$(JAVA_LIB_FILE)
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SHARED_LDFLAGS += -install_name $(LIBDIR)/$(SHARED_LIB_FILE)
|
||||
JAVA_SHARED_LDFLAGS := -dynamiclib -install_name $(LIBDIR)/$(JAVA_LIB_FILE)
|
||||
else
|
||||
JAVA_SHARED_LDFLAGS := $(SHARED_LDFLAGS)
|
||||
endif
|
||||
|
||||
JAVA_HOME ?= $(shell /usr/libexec/java_home 2>/dev/null || sh -c 'J=$$(command -v javac 2>/dev/null); if [ -n "$$J" ]; then dirname "$$(dirname "$$(readlink -f "$$J")")"; fi')
|
||||
JAVA_INCLUDE := -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/$(JAVA_OS_INCLUDE)
|
||||
|
||||
ifneq ($(NR34LIB),ON)
|
||||
CPPFLAGS += -I third_party/rnnoise/include -I third_party/libspecbleach/include
|
||||
NR34_DEPS := third_party/rnnoise/librnnoise.a third_party/libspecbleach/libspecbleach.a
|
||||
NR34_LIBS := $(NR34_DEPS) $(FFTWLIBS_F)
|
||||
else
|
||||
NR34_DEPS :=
|
||||
NR34_LIBS :=
|
||||
endif
|
||||
|
||||
COMMON_LIBS := $(FFTWLIBS) -lpthread -lm
|
||||
|
||||
COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(PIC_CFLAGS) $(FFTWINCLUDE)
|
||||
|
||||
SOURCES = amd.c \
|
||||
ammod.c \
|
||||
amsq.c \
|
||||
analyzer.c \
|
||||
anf.c \
|
||||
anr.c \
|
||||
apfshadow.c \
|
||||
bandpass.c \
|
||||
calcc.c \
|
||||
calculus.c \
|
||||
cblock.c \
|
||||
cfcomp.c \
|
||||
cfir.c \
|
||||
channel.c \
|
||||
cmath.c \
|
||||
compress.c \
|
||||
delay.c \
|
||||
dexp.c \
|
||||
div.c \
|
||||
doublepole.c \
|
||||
eer.c \
|
||||
emnr.c \
|
||||
emph.c \
|
||||
eq.c \
|
||||
fcurve.c \
|
||||
FDnoiseIQ.c \
|
||||
fir.c \
|
||||
firmin.c \
|
||||
fmd.c \
|
||||
fmmod.c \
|
||||
fmsq.c \
|
||||
gain.c \
|
||||
gaussian.c \
|
||||
gen.c \
|
||||
icfir.c \
|
||||
iir.c \
|
||||
impulse_cache.c \
|
||||
iobuffs.c \
|
||||
iqc.c \
|
||||
linux_port.c \
|
||||
lmath.c \
|
||||
main.c \
|
||||
matchedCW.c \
|
||||
meter.c \
|
||||
meterlog10.c \
|
||||
nbp.c \
|
||||
nob.c \
|
||||
nobII.c \
|
||||
osctrl.c \
|
||||
patchpanel.c \
|
||||
resample.c \
|
||||
rmatch.c \
|
||||
rnnr.c \
|
||||
RXA.c \
|
||||
sbnr.c \
|
||||
sender.c \
|
||||
shift.c \
|
||||
siphon.c \
|
||||
slew.c \
|
||||
snb.c \
|
||||
ssql.c \
|
||||
syncbuffs.c \
|
||||
TXA.c \
|
||||
utilities.c \
|
||||
varsamp.c \
|
||||
version.c \
|
||||
wcpAGC.c \
|
||||
wisdom.c \
|
||||
zetaHat.c
|
||||
|
||||
OBJS = $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o))
|
||||
|
||||
JAVA_SOURCES = org_openhpsdr_dsp_Wdsp.c
|
||||
JAVA_OBJS = $(addprefix $(OBJDIR)/, $(JAVA_SOURCES:.c=.o))
|
||||
JAVA_CLASS = java/org/openhpsdr/dsp/Wdsp.java
|
||||
JAVA_BUILD_DIR = java/build
|
||||
|
||||
.PHONY: all static shared java java-classes android install install_java install-dirs clean release
|
||||
|
||||
all: static shared java
|
||||
|
||||
static: $(STATIC_LIB)
|
||||
|
||||
shared: $(SHARED_LIB)
|
||||
|
||||
java:
|
||||
ifeq ($(strip $(JAVA_HOME)),)
|
||||
@echo "Skipping Java build: JAVA_HOME is not set and javac was not found in PATH"
|
||||
else
|
||||
@$(MAKE) $(JAVA_LIB) java-classes
|
||||
endif
|
||||
|
||||
android:
|
||||
$(MAKE) -f Makefile.android all
|
||||
|
||||
$(OUTDIR) $(OBJDIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(STATIC_LIB): $(OBJS) | $(OUTDIR)
|
||||
$(AR) rv $@ $(OBJS)
|
||||
$(RANLIB) $@
|
||||
|
||||
$(SHARED_LIB): $(OBJS) $(NR34_DEPS) | $(OUTDIR)
|
||||
$(CC) $(SHARED_LDFLAGS) $(NOEXECSTACK) $(LDFLAGS) -o $@ $(OBJS) $(NR34_LIBS) $(COMMON_LIBS)
|
||||
|
||||
$(JAVA_LIB): $(JAVA_OBJS) $(SHARED_LIB) | $(OUTDIR)
|
||||
$(CC) $(JAVA_SHARED_LDFLAGS) $(NOEXECSTACK) $(LDFLAGS) -o $@ $(JAVA_OBJS) -L$(OUTDIR) -lwdsp
|
||||
|
||||
java-classes: $(JAVA_CLASS)
|
||||
@mkdir -p $(JAVA_BUILD_DIR)
|
||||
javac -d $(JAVA_BUILD_DIR) $(JAVA_CLASS)
|
||||
|
||||
third_party/rnnoise/librnnoise.a:
|
||||
$(MAKE) -C third_party/rnnoise CFLAGS="$(CFLAGS) $(PIC_CFLAGS) -Iinclude -Isrc"
|
||||
|
||||
third_party/libspecbleach/libspecbleach.a:
|
||||
$(MAKE) -C third_party/libspecbleach CFLAGS="$(CFLAGS) $(PIC_CFLAGS) $(shell pkg-config --cflags fftw3f 2>/dev/null)"
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(COMPILE) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/org_openhpsdr_dsp_Wdsp.o: org_openhpsdr_dsp_Wdsp.c org_openhpsdr_dsp_Wdsp.h | $(OBJDIR)
|
||||
@test -n "$(JAVA_HOME)" || (echo "JAVA_HOME is required to build JNI object org_openhpsdr_dsp_Wdsp.o"; exit 2)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(JNI_CFLAGS) $(PIC_CFLAGS) $(FFTWINCLUDE) $(JAVA_INCLUDE) -c -o $@ $<
|
||||
|
||||
install-dirs:
|
||||
mkdir -p $(LIBDIR) $(INCLUDEDIR)
|
||||
|
||||
install: $(STATIC_LIB) $(SHARED_LIB) install-dirs
|
||||
cp wdsp.h $(INCLUDEDIR)
|
||||
cp $(STATIC_LIB) $(LIBDIR)
|
||||
cp $(SHARED_LIB) $(LIBDIR)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
/usr/bin/install_name_tool -id $(LIBDIR)/$(SHARED_LIB_FILE) $(LIBDIR)/$(SHARED_LIB_FILE)
|
||||
else
|
||||
ldconfig || :
|
||||
endif
|
||||
|
||||
install_java: $(JAVA_LIB) install-dirs
|
||||
cp $(JAVA_LIB) $(LIBDIR)
|
||||
|
||||
release: $(SHARED_LIB)
|
||||
cp $(SHARED_LIB) ../pihpsdr/release/pihpsdr
|
||||
|
||||
clean:
|
||||
-rm -rf $(OBJDIR) $(OUTDIR)
|
||||
-rm -rf $(JAVA_BUILD_DIR)
|
||||
-$(MAKE) -C third_party/rnnoise clean
|
||||
-$(MAKE) -C third_party/libspecbleach clean
|
||||
313
Makefile.android
Normal file
313
Makefile.android
Normal file
@ -0,0 +1,313 @@
|
||||
# Android cross-build for WDSP and JNI bridge
|
||||
# Usage:
|
||||
# make android
|
||||
# make -f Makefile.android all
|
||||
# Optional overrides:
|
||||
# ANDROID_NDK=/path/to/ndk ANDROID_API=24 ANDROID_ABIS="arm64-v8a x86_64"
|
||||
#
|
||||
# Requires FFTW source in third_party/fftw/
|
||||
# Download from https://www.fftw.org/download.html and extract so that
|
||||
# third_party/fftw/configure exists.
|
||||
|
||||
ANDROID_NDK ?= /home/vladimir/Android/Sdk/ndk/29.0.14206865
|
||||
ANDROID_API ?= 24
|
||||
ANDROID_ABIS ?= arm64-v8a armeabi-v7a x86_64
|
||||
ANDROID_HOST_TAG ?= $(shell uname -s | tr '[:upper:]' '[:lower:]' | sed 's/darwin/darwin/;s/linux/linux/')-$(shell uname -m | sed 's/aarch64/arm64/;s/x86_64/x86_64/')
|
||||
|
||||
JBR_BIN ?= /opt/android-studio/jbr/bin
|
||||
JAVAC ?= $(JBR_BIN)/javac
|
||||
|
||||
TOOLCHAIN := $(ANDROID_NDK)/toolchains/llvm/prebuilt/$(ANDROID_HOST_TAG)
|
||||
|
||||
COMMON_CFLAGS ?= -O3 -D_GNU_SOURCE -Wno-parentheses -fPIC
|
||||
COMMON_CPPFLAGS ?= -I. -I third_party/rnnoise/include -I third_party/libspecbleach/include
|
||||
ANDROID_JNI_CFLAGS ?= -std=gnu89 -Wno-implicit-function-declaration -Wno-int-conversion \
|
||||
-Wno-incompatible-pointer-types -Wno-incompatible-pointer-types-discards-qualifiers
|
||||
|
||||
FFTW_SRC ?= third_party/fftw
|
||||
FFTW_MAKEJOBS ?= $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||
|
||||
WDSP_SOURCES = amd.c \
|
||||
ammod.c \
|
||||
amsq.c \
|
||||
analyzer.c \
|
||||
anf.c \
|
||||
anr.c \
|
||||
apfshadow.c \
|
||||
bandpass.c \
|
||||
calcc.c \
|
||||
calculus.c \
|
||||
cblock.c \
|
||||
cfcomp.c \
|
||||
cfir.c \
|
||||
channel.c \
|
||||
cmath.c \
|
||||
compress.c \
|
||||
delay.c \
|
||||
dexp.c \
|
||||
div.c \
|
||||
doublepole.c \
|
||||
eer.c \
|
||||
emnr.c \
|
||||
emph.c \
|
||||
eq.c \
|
||||
fcurve.c \
|
||||
FDnoiseIQ.c \
|
||||
fir.c \
|
||||
firmin.c \
|
||||
fmd.c \
|
||||
fmmod.c \
|
||||
fmsq.c \
|
||||
gain.c \
|
||||
gaussian.c \
|
||||
gen.c \
|
||||
icfir.c \
|
||||
iir.c \
|
||||
impulse_cache.c \
|
||||
iobuffs.c \
|
||||
iqc.c \
|
||||
linux_port.c \
|
||||
lmath.c \
|
||||
main.c \
|
||||
matchedCW.c \
|
||||
meter.c \
|
||||
meterlog10.c \
|
||||
nbp.c \
|
||||
nob.c \
|
||||
nobII.c \
|
||||
osctrl.c \
|
||||
patchpanel.c \
|
||||
resample.c \
|
||||
rmatch.c \
|
||||
rnnr.c \
|
||||
RXA.c \
|
||||
sbnr.c \
|
||||
sender.c \
|
||||
shift.c \
|
||||
siphon.c \
|
||||
slew.c \
|
||||
snb.c \
|
||||
ssql.c \
|
||||
syncbuffs.c \
|
||||
TXA.c \
|
||||
utilities.c \
|
||||
varsamp.c \
|
||||
version.c \
|
||||
wcpAGC.c \
|
||||
wisdom.c \
|
||||
zetaHat.c
|
||||
|
||||
RNNOISE_SOURCES = \
|
||||
third_party/rnnoise/src/denoise.c \
|
||||
third_party/rnnoise/src/celt_lpc.c \
|
||||
third_party/rnnoise/src/kiss_fft.c \
|
||||
third_party/rnnoise/src/nnet.c \
|
||||
third_party/rnnoise/src/nnet_default.c \
|
||||
third_party/rnnoise/src/parse_lpcnet_weights.c \
|
||||
third_party/rnnoise/src/pitch.c \
|
||||
third_party/rnnoise/src/rnn.c \
|
||||
third_party/rnnoise/src/rnnoise_data.c \
|
||||
third_party/rnnoise/src/rnnoise_data_1.c \
|
||||
third_party/rnnoise/src/rnnoise_data_2.c \
|
||||
third_party/rnnoise/src/rnnoise_data_3.c \
|
||||
third_party/rnnoise/src/rnnoise_data_4.c \
|
||||
third_party/rnnoise/src/rnnoise_data_5.c \
|
||||
third_party/rnnoise/src/rnnoise_data_6.c \
|
||||
third_party/rnnoise/src/rnnoise_tables.c
|
||||
|
||||
SPECBLEACH_SOURCES = \
|
||||
third_party/libspecbleach/src/processors/specbleach_adenoiser.c \
|
||||
third_party/libspecbleach/src/processors/specbleach_denoiser.c \
|
||||
third_party/libspecbleach/src/processors/adaptivedenoiser/adaptive_denoiser.c \
|
||||
third_party/libspecbleach/src/shared/gain_estimation/gain_estimators.c \
|
||||
third_party/libspecbleach/src/shared/noise_estimation/adaptive_noise_estimator.c \
|
||||
third_party/libspecbleach/src/shared/pre_estimation/absolute_hearing_thresholds.c \
|
||||
third_party/libspecbleach/src/shared/pre_estimation/critical_bands.c \
|
||||
third_party/libspecbleach/src/shared/pre_estimation/masking_estimator.c \
|
||||
third_party/libspecbleach/src/shared/pre_estimation/noise_scaling_criterias.c \
|
||||
third_party/libspecbleach/src/shared/pre_estimation/spectral_smoother.c \
|
||||
third_party/libspecbleach/src/shared/pre_estimation/transient_detector.c \
|
||||
third_party/libspecbleach/src/shared/post_estimation/noise_floor_manager.c \
|
||||
third_party/libspecbleach/src/shared/post_estimation/postfilter.c \
|
||||
third_party/libspecbleach/src/shared/post_estimation/spectral_whitening.c \
|
||||
third_party/libspecbleach/src/shared/utils/denoise_mixer.c \
|
||||
third_party/libspecbleach/src/shared/utils/general_utils.c \
|
||||
third_party/libspecbleach/src/shared/utils/spectral_features.c \
|
||||
third_party/libspecbleach/src/shared/utils/spectral_utils.c \
|
||||
third_party/libspecbleach/src/shared/stft/stft_processor.c \
|
||||
third_party/libspecbleach/src/shared/stft/fft_transform.c \
|
||||
third_party/libspecbleach/src/shared/stft/stft_buffer.c \
|
||||
third_party/libspecbleach/src/shared/stft/stft_windows.c
|
||||
|
||||
ALL_C_SOURCES = $(WDSP_SOURCES) $(RNNOISE_SOURCES) $(SPECBLEACH_SOURCES)
|
||||
|
||||
JNI_SOURCE = org_openhpsdr_dsp_Wdsp.c
|
||||
JAVA_SOURCE = java/org/openhpsdr/dsp/Wdsp.java
|
||||
|
||||
ANDROID_OBJ_ROOT := obj/android
|
||||
ANDROID_LIB_ROOT := lib/android
|
||||
ANDROID_JAVA_OUT := lib/android/java
|
||||
|
||||
.PHONY: all clean java-classes check-fftw-src
|
||||
|
||||
all: check-fftw-src \
|
||||
$(foreach abi,$(ANDROID_ABIS),\
|
||||
$(ANDROID_LIB_ROOT)/$(abi)/libfftw3.so \
|
||||
$(ANDROID_LIB_ROOT)/$(abi)/libfftw3f.so \
|
||||
$(ANDROID_LIB_ROOT)/$(abi)/libwdsp.so \
|
||||
$(ANDROID_LIB_ROOT)/$(abi)/libwdspj.so) \
|
||||
java-classes
|
||||
|
||||
check-fftw-src:
|
||||
@test -f $(FFTW_SRC)/configure || { \
|
||||
echo ""; \
|
||||
echo "ERROR: FFTW source not found at $(FFTW_SRC)/configure"; \
|
||||
echo "Download and extract FFTW from https://www.fftw.org/download.html"; \
|
||||
echo "so that $(FFTW_SRC)/configure exists."; \
|
||||
echo ""; \
|
||||
exit 1; \
|
||||
}
|
||||
|
||||
# abi_target: compiler triple for NDK clang wrapper
|
||||
define abi_target
|
||||
$(if $(filter $(1),arm64-v8a),aarch64-linux-android,\
|
||||
$(if $(filter $(1),armeabi-v7a),armv7a-linux-androideabi,\
|
||||
$(if $(filter $(1),x86_64),x86_64-linux-android,unsupported)))
|
||||
endef
|
||||
|
||||
# fftw_host: --host value for FFTW configure (autoconf canonical names)
|
||||
define fftw_host
|
||||
$(if $(filter $(1),arm64-v8a),aarch64-linux-android,\
|
||||
$(if $(filter $(1),armeabi-v7a),arm-linux-androideabi,\
|
||||
$(if $(filter $(1),x86_64),x86_64-linux-android,$(1))))
|
||||
endef
|
||||
|
||||
# abi_cflags: extra CFLAGS per ABI
|
||||
define abi_cflags
|
||||
$(if $(filter $(1),armeabi-v7a),-march=armv7-a -mfloat-abi=softfp -mfpu=neon,)
|
||||
endef
|
||||
|
||||
# fftw3 (double) SIMD flags per ABI
|
||||
define fftw_simd
|
||||
$(if $(filter $(1),x86_64),--enable-sse2,)
|
||||
endef
|
||||
|
||||
# fftw3f (float) SIMD flags per ABI
|
||||
define fftwf_simd
|
||||
$(if $(filter $(1),arm64-v8a),--enable-neon,\
|
||||
$(if $(filter $(1),armeabi-v7a),--enable-neon,\
|
||||
$(if $(filter $(1),x86_64),--enable-sse --enable-sse2,)))
|
||||
endef
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Per-ABI rules
|
||||
# -----------------------------------------------------------------------
|
||||
define make_abi_rules
|
||||
|
||||
ABI_$(1)_TARGET := $(call abi_target,$(1))
|
||||
ABI_$(1)_CC := $(TOOLCHAIN)/bin/$$(ABI_$(1)_TARGET)$(ANDROID_API)-clang
|
||||
ABI_$(1)_OBJDIR := $(ANDROID_OBJ_ROOT)/$(1)
|
||||
ABI_$(1)_LIBDIR := $(ANDROID_LIB_ROOT)/$(1)
|
||||
ABI_$(1)_CFLAGS := $(COMMON_CFLAGS) $(call abi_cflags,$(1))
|
||||
|
||||
# FFTW install prefixes (inside obj tree)
|
||||
ABI_$(1)_FFTW_PREFIX := $$(ABI_$(1)_OBJDIR)/fftw-install
|
||||
ABI_$(1)_FFTWF_PREFIX := $$(ABI_$(1)_OBJDIR)/fftwf-install
|
||||
|
||||
# Stamp files (avoid re-running configure+make on every build)
|
||||
ABI_$(1)_FFTW_STAMP := $$(ABI_$(1)_FFTW_PREFIX)/.built
|
||||
ABI_$(1)_FFTWF_STAMP := $$(ABI_$(1)_FFTWF_PREFIX)/.built
|
||||
|
||||
ABI_$(1)_CPPFLAGS := $(COMMON_CPPFLAGS) \
|
||||
-I third_party/rnnoise/src -I third_party/libspecbleach/include -I third_party/libspecbleach/src -I third_party/libspecbleach/src/shared \
|
||||
-I$$(ABI_$(1)_FFTW_PREFIX)/include
|
||||
|
||||
ABI_$(1)_WDSP_OBJS := $$(patsubst %.c,$$(ABI_$(1)_OBJDIR)/%.o,$$(ALL_C_SOURCES))
|
||||
ABI_$(1)_JNI_OBJ := $$(ABI_$(1)_OBJDIR)/$(JNI_SOURCE:.c=.o)
|
||||
|
||||
# --- Build fftw3 (double) ---
|
||||
$$(ABI_$(1)_FFTW_STAMP):
|
||||
@mkdir -p $$(ABI_$(1)_OBJDIR)/fftw-build $$(ABI_$(1)_FFTW_PREFIX)
|
||||
cd $$(ABI_$(1)_OBJDIR)/fftw-build && \
|
||||
$(abspath $(FFTW_SRC))/configure \
|
||||
--host=$(call fftw_host,$(1)) \
|
||||
CC="$$(ABI_$(1)_CC)" \
|
||||
CFLAGS="-fPIC" \
|
||||
--prefix=$(abspath $$(ABI_$(1)_FFTW_PREFIX)) \
|
||||
--enable-static --disable-shared --disable-fortran \
|
||||
$(call fftw_simd,$(1)) && \
|
||||
$(MAKE) -j$(FFTW_MAKEJOBS) install
|
||||
@touch $$@
|
||||
|
||||
# --- Build fftw3f (float/single) ---
|
||||
$$(ABI_$(1)_FFTWF_STAMP):
|
||||
@mkdir -p $$(ABI_$(1)_OBJDIR)/fftwf-build $$(ABI_$(1)_FFTWF_PREFIX)
|
||||
cd $$(ABI_$(1)_OBJDIR)/fftwf-build && \
|
||||
$(abspath $(FFTW_SRC))/configure \
|
||||
--host=$(call fftw_host,$(1)) \
|
||||
CC="$$(ABI_$(1)_CC)" \
|
||||
CFLAGS="-fPIC" \
|
||||
--prefix=$(abspath $$(ABI_$(1)_FFTWF_PREFIX)) \
|
||||
--enable-static --disable-shared --disable-fortran \
|
||||
--enable-float \
|
||||
$(call fftwf_simd,$(1)) && \
|
||||
$(MAKE) -j$(FFTW_MAKEJOBS) install
|
||||
@touch $$@
|
||||
|
||||
# --- Shared fftw3.so / fftw3f.so for packaging alongside libwdsp.so ---
|
||||
$$(ABI_$(1)_LIBDIR)/libfftw3.so: $$(ABI_$(1)_FFTW_STAMP)
|
||||
@mkdir -p $$(@D)
|
||||
$$(ABI_$(1)_CC) -shared -Wl,-soname,libfftw3.so \
|
||||
-Wl,--whole-archive $$(ABI_$(1)_FFTW_PREFIX)/lib/libfftw3.a -Wl,--no-whole-archive \
|
||||
-lm -o $$@
|
||||
|
||||
$$(ABI_$(1)_LIBDIR)/libfftw3f.so: $$(ABI_$(1)_FFTWF_STAMP)
|
||||
@mkdir -p $$(@D)
|
||||
$$(ABI_$(1)_CC) -shared -Wl,-soname,libfftw3f.so \
|
||||
-Wl,--whole-archive $$(ABI_$(1)_FFTWF_PREFIX)/lib/libfftw3f.a -Wl,--no-whole-archive \
|
||||
-lm -o $$@
|
||||
|
||||
# --- Compile WDSP + rnnoise + specbleach sources ---
|
||||
$$(ABI_$(1)_OBJDIR)/%.o: %.c | $$(ABI_$(1)_FFTW_STAMP) $$(ABI_$(1)_FFTWF_STAMP)
|
||||
@mkdir -p $$(@D)
|
||||
$$(ABI_$(1)_CC) $$(ABI_$(1)_CPPFLAGS) $$(ABI_$(1)_CFLAGS) -c -o $$@ $$<
|
||||
|
||||
# --- Build libwdsp.so (statically links fftw) ---
|
||||
$$(ABI_$(1)_LIBDIR)/libwdsp.so: $$(ABI_$(1)_WDSP_OBJS) $$(ABI_$(1)_FFTW_STAMP) $$(ABI_$(1)_FFTWF_STAMP)
|
||||
@mkdir -p $$(@D)
|
||||
$$(ABI_$(1)_CC) -shared -Wl,-soname,libwdsp.so -o $$@ \
|
||||
$$(ABI_$(1)_WDSP_OBJS) \
|
||||
$$(ABI_$(1)_FFTW_PREFIX)/lib/libfftw3.a \
|
||||
$$(ABI_$(1)_FFTWF_PREFIX)/lib/libfftw3f.a \
|
||||
-lm -llog
|
||||
|
||||
# --- Compile JNI bridge ---
|
||||
$$(ABI_$(1)_JNI_OBJ): $(JNI_SOURCE) org_openhpsdr_dsp_Wdsp.h | $$(ABI_$(1)_FFTW_STAMP)
|
||||
@mkdir -p $$(@D)
|
||||
$$(ABI_$(1)_CC) $$(ABI_$(1)_CPPFLAGS) $$(ABI_$(1)_CFLAGS) $(ANDROID_JNI_CFLAGS) \
|
||||
-I$(TOOLCHAIN)/sysroot/usr/include -c -o $$@ $$<
|
||||
|
||||
# --- Build libwdspj.so ---
|
||||
$$(ABI_$(1)_LIBDIR)/libwdspj.so: $$(ABI_$(1)_JNI_OBJ) $$(ABI_$(1)_LIBDIR)/libwdsp.so
|
||||
@mkdir -p $$(@D)
|
||||
$$(ABI_$(1)_CC) -shared -Wl,-soname,libwdspj.so -o $$@ \
|
||||
$$(ABI_$(1)_JNI_OBJ) \
|
||||
-L$$(ABI_$(1)_LIBDIR) -lwdsp -llog
|
||||
|
||||
endef
|
||||
|
||||
$(foreach abi,$(ANDROID_ABIS),$(eval $(call make_abi_rules,$(abi))))
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
java-classes: $(JAVA_SOURCE)
|
||||
@if [ -x "$(JAVAC)" ]; then \
|
||||
mkdir -p "$(ANDROID_JAVA_OUT)"; \
|
||||
"$(JAVAC)" -d "$(ANDROID_JAVA_OUT)" "$(JAVA_SOURCE)"; \
|
||||
echo "Java classes built in $(ANDROID_JAVA_OUT)"; \
|
||||
else \
|
||||
echo "Skipping Java class build: javac not found at $(JAVAC)"; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -rf $(ANDROID_OBJ_ROOT) $(ANDROID_LIB_ROOT)
|
||||
179
README.md
Normal file
179
README.md
Normal file
@ -0,0 +1,179 @@
|
||||
# WDSP
|
||||
|
||||
DSP library for SDR (Software Defined Radio) applications.
|
||||
|
||||
Originally written for Windows by Warren Pratt, NR0V.
|
||||
Ported to Linux and Android by John Melton G0ORX/N6LYT.
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
wdsp/
|
||||
├── *.c / *.h — WDSP library sources
|
||||
├── org_openhpsdr_dsp_Wdsp.c/.h — JNI bridge
|
||||
├── Makefile — host build (Linux / macOS)
|
||||
├── Makefile.android — Android cross-build
|
||||
├── java/
|
||||
│ └── org/openhpsdr/dsp/Wdsp.java
|
||||
├── third_party/
|
||||
│ ├── fftw/ — FFTW source (download manually, see below)
|
||||
│ ├── rnnoise/ — RNNoise noise suppression
|
||||
│ └── libspecbleach/ — spectral noise reduction
|
||||
├── obj/ — object files (generated)
|
||||
└── lib/ — built libraries (generated)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Host build (Linux / macOS)
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
sudo apt install build-essential libfftw3-dev pkg-config default-jdk
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install fftw pkg-config
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# Full build: static + shared + JNI library + Java classes
|
||||
make
|
||||
|
||||
# Individual targets
|
||||
make static # lib/libwdsp.a
|
||||
make shared # lib/libwdsp.so (or .dylib on macOS)
|
||||
make java # lib/libwdspj.so + java/build/
|
||||
```
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
sudo make install # installs to /usr/local/lib and /usr/local/include
|
||||
sudo make PREFIX=/opt/wdsp install # custom prefix
|
||||
sudo make install_java # install libwdspj alongside libwdsp
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `PREFIX` | `/usr/local` | Install prefix |
|
||||
| `NR34LIB` | off | Set to `ON` to skip bundled rnnoise/libspecbleach |
|
||||
| `CC` | `gcc` | C compiler |
|
||||
| `CFLAGS` | `-pthread -O3 ...` | Compiler flags |
|
||||
|
||||
```bash
|
||||
make NR34LIB=ON # build without NR3/NR4 noise reduction
|
||||
make CC=clang
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Android build
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Android NDK** r23 or newer
|
||||
2. **FFTW source** — download and extract into `third_party/fftw/`:
|
||||
|
||||
```bash
|
||||
wget https://www.fftw.org/fftw-3.3.10.tar.gz
|
||||
tar xf fftw-3.3.10.tar.gz
|
||||
mv fftw-3.3.10 third_party/fftw
|
||||
```
|
||||
|
||||
3. **Java compiler** — for building the `.class` file (Android Studio's JBR or any JDK)
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
make android
|
||||
# or directly
|
||||
make -f Makefile.android
|
||||
```
|
||||
|
||||
Output is placed into:
|
||||
|
||||
```
|
||||
lib/android/
|
||||
├── arm64-v8a/
|
||||
│ ├── libfftw3.so
|
||||
│ ├── libfftw3f.so
|
||||
│ ├── libwdsp.so
|
||||
│ └── libwdspj.so
|
||||
├── armeabi-v7a/
|
||||
│ └── ...
|
||||
├── x86_64/
|
||||
│ └── ...
|
||||
└── java/
|
||||
└── org/openhpsdr/dsp/Wdsp.class
|
||||
```
|
||||
|
||||
`libwdsp.so` has FFTW linked statically — no separate FFTW dependency at runtime on the device.
|
||||
|
||||
### Options
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `ANDROID_NDK` | `/home/vladimir/Android/Sdk/ndk/29.0.14206865` | Path to Android NDK |
|
||||
| `ANDROID_API` | `24` | Minimum API level |
|
||||
| `ANDROID_ABIS` | `arm64-v8a armeabi-v7a x86_64` | Target ABIs |
|
||||
| `ANDROID_HOST_TAG` | auto-detected | Host platform (`linux-x86_64`, `darwin-x86_64`, etc.) |
|
||||
| `FFTW_SRC` | `third_party/fftw` | Path to FFTW source |
|
||||
| `JAVAC` | `/opt/android-studio/jbr/bin/javac` | Java compiler |
|
||||
|
||||
```bash
|
||||
make -f Makefile.android \
|
||||
ANDROID_NDK=~/Android/Sdk/ndk/26.3.11579264 \
|
||||
ANDROID_API=26 \
|
||||
ANDROID_ABIS=arm64-v8a
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
make -f Makefile.android clean
|
||||
# removes obj/android/ and lib/android/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using in an Android project
|
||||
|
||||
Copy `lib/android/{abi}/` into your Android project's `jniLibs`:
|
||||
|
||||
```
|
||||
app/src/main/jniLibs/
|
||||
├── arm64-v8a/
|
||||
│ ├── libwdsp.so
|
||||
│ └── libwdspj.so
|
||||
├── armeabi-v7a/
|
||||
│ └── ...
|
||||
└── x86_64/
|
||||
└── ...
|
||||
```
|
||||
|
||||
Add `Wdsp.java` to your source tree and load the libraries at startup:
|
||||
|
||||
```java
|
||||
System.loadLibrary("wdsp");
|
||||
System.loadLibrary("wdspj");
|
||||
```
|
||||
|
||||
Or use the singleton:
|
||||
|
||||
```java
|
||||
Wdsp wdsp = Wdsp.getInstance();
|
||||
```
|
||||
226
RXA.h
Normal file
226
RXA.h
Normal file
@ -0,0 +1,226 @@
|
||||
/* RXA.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2014, 2015, 2016, 2025 Warren Pratt, NR0V
|
||||
|
||||
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 _rxa_h
|
||||
#define _rxa_h
|
||||
#include "comm.h"
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct _rxa
|
||||
{
|
||||
double* inbuff;
|
||||
double* outbuff;
|
||||
double* midbuff;
|
||||
int mode;
|
||||
double meter[RXA_METERTYPE_LAST];
|
||||
CRITICAL_SECTION* 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
|
||||
{
|
||||
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
|
||||
{
|
||||
APFSHADOW p;
|
||||
} apfshadow;
|
||||
struct
|
||||
{
|
||||
DOUBLEPOLE p;
|
||||
} doublepole;
|
||||
struct
|
||||
{
|
||||
MATCHED p;
|
||||
} matched;
|
||||
struct
|
||||
{
|
||||
GAUSSIAN p;
|
||||
} gaussian;
|
||||
struct
|
||||
{
|
||||
RNNR p; // NR3 + NR4 support (nr3)
|
||||
} rnnr;
|
||||
struct
|
||||
{
|
||||
SBNR p; // NR3 + NR4 support (nr4)
|
||||
} sbnr;
|
||||
|
||||
struct
|
||||
{
|
||||
SPEAK p;
|
||||
} speak;
|
||||
struct
|
||||
{
|
||||
MPEAK p;
|
||||
} mpeak;
|
||||
struct
|
||||
{
|
||||
PANEL p;
|
||||
} panel;
|
||||
struct
|
||||
{
|
||||
SIPHON p;
|
||||
} sip1;
|
||||
struct
|
||||
{
|
||||
CBL p;
|
||||
} cbl;
|
||||
struct
|
||||
{
|
||||
SSQL p;
|
||||
} ssql;
|
||||
};
|
||||
|
||||
extern struct _rxa rxa[];
|
||||
|
||||
extern void create_rxa (int channel);
|
||||
|
||||
extern void destroy_rxa (int channel);
|
||||
|
||||
extern void flush_rxa (int channel);
|
||||
|
||||
extern void xrxa (int channel);
|
||||
|
||||
extern void setInputSamplerate_rxa (int channel);
|
||||
|
||||
extern void setOutputSamplerate_rxa (int channel);
|
||||
|
||||
extern void setDSPSamplerate_rxa (int channel);
|
||||
|
||||
extern void setDSPBuffsize_rxa (int channel);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAMode (int channel, int mode);
|
||||
|
||||
extern void RXAResCheck (int channel);
|
||||
|
||||
extern void RXAbp1Check (int channel, int amd_run, int snba_run, int emnr_run, int anf_run, int anr_run, int rnnr_run, int sbnr_run); // NR3 + NR4 support
|
||||
|
||||
extern void RXAbp1Set (int channel);
|
||||
|
||||
extern void RXAbpsnbaCheck (int channel, int mode, int notch_run);
|
||||
|
||||
extern void RXAbpsnbaSet (int channel);
|
||||
|
||||
#endif
|
||||
935
TXA.c
Normal file
935
TXA.c
Normal file
@ -0,0 +1,935 @@
|
||||
/* 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
|
||||
|
||||
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.h"
|
||||
|
||||
struct _txa txa[MAX_CHANNELS];
|
||||
|
||||
void create_txa (int channel)
|
||||
{
|
||||
txa[channel].mode = TXA_LSB;
|
||||
txa[channel].f_low = -5000.0;
|
||||
txa[channel].f_high = - 100.0;
|
||||
txa[channel].inbuff = (double *) malloc0 (1 * ch[channel].dsp_insize * sizeof (complex));
|
||||
txa[channel].outbuff = (double *) malloc0 (1 * ch[channel].dsp_outsize * sizeof (complex));
|
||||
txa[channel].midbuff = (double *) malloc0 (2 * ch[channel].dsp_size * sizeof (complex));
|
||||
|
||||
txa[channel].rsmpin.p = create_resample (
|
||||
0, // run - will be turned on below if needed
|
||||
ch[channel].dsp_insize, // input buffer size
|
||||
txa[channel].inbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
ch[channel].in_rate, // input sample rate
|
||||
ch[channel].dsp_rate, // output sample rate
|
||||
0.0, // select cutoff automatically
|
||||
0, // select ncoef automatically
|
||||
1.0); // gain
|
||||
|
||||
txa[channel].gen0.p = create_gen (
|
||||
0, // run
|
||||
ch[channel].dsp_size, // buffer size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
ch[channel].dsp_rate, // sample rate
|
||||
2); // mode
|
||||
|
||||
txa[channel].panel.p = create_panel (
|
||||
channel, // channel number
|
||||
1, // run
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].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[channel].phrot.p = create_phrot (
|
||||
0, // run
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
338.0, // 1/2 of phase frequency
|
||||
8); // number of stages
|
||||
|
||||
txa[channel].micmeter.p = create_meter (
|
||||
1, // run
|
||||
0, // optional pointer to another 'run'
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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[channel].amsq.p = create_amsq (
|
||||
0, // run
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
txa[channel].midbuff, // trigger buffer
|
||||
ch[channel].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[channel].eqp.p = create_eqp (
|
||||
0, // run - OFF by default
|
||||
ch[channel].dsp_size, // size
|
||||
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||
0, // minimum phase flag
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
10, // nfreqs
|
||||
default_F, // vector of frequencies
|
||||
default_G, // vector of gain values
|
||||
0, // cutoff mode
|
||||
0, // wintype
|
||||
ch[channel].dsp_rate); // samplerate
|
||||
}
|
||||
|
||||
txa[channel].eqmeter.p = create_meter (
|
||||
1, // run
|
||||
&(txa[channel].eqp.p->run), // pointer to eqp 'run'
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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[channel].preemph.p = create_emphp (
|
||||
0, // run
|
||||
1, // position
|
||||
ch[channel].dsp_size, // size
|
||||
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||
0, // minimum phase flag
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer,
|
||||
ch[channel].dsp_rate, // sample rate
|
||||
0, // pre-emphasis type
|
||||
300.0, // f_low
|
||||
3000.0); // f_high
|
||||
|
||||
txa[channel].leveler.p = create_wcpagc (
|
||||
0, // run - OFF by default
|
||||
5, // mode
|
||||
0, // 0 for max(I,Q), 1 for envelope
|
||||
txa[channel].midbuff, // input buff pointer
|
||||
txa[channel].midbuff, // output buff pointer
|
||||
ch[channel].dsp_size, // io_buffsize
|
||||
ch[channel].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[channel].lvlrmeter.p = create_meter (
|
||||
1, // run
|
||||
&(txa[channel].leveler.p->run), // pointer to leveler 'run'
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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[channel].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[channel].cfcomp.p = create_cfcomp(
|
||||
0, // run
|
||||
0, // position
|
||||
0, // post-equalizer run
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
2048, // fft size
|
||||
4, // overlap
|
||||
ch[channel].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[channel].cfcmeter.p = create_meter (
|
||||
1, // run
|
||||
&(txa[channel].cfcomp.p->run), // pointer to eqp 'run'
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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[channel].cfcomp.p->gain); // pointer for gain computation
|
||||
|
||||
txa[channel].bp0.p = create_bandpass (
|
||||
1, // always runs
|
||||
0, // position
|
||||
ch[channel].dsp_size, // size
|
||||
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||
0, // flag for minimum phase
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
txa[channel].f_low, // low freq cutoff
|
||||
txa[channel].f_high, // high freq cutoff
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
1, // wintype
|
||||
2.0); // gain
|
||||
|
||||
txa[channel].compressor.p = create_compressor (
|
||||
0, // run - OFF by default
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
3.0); // gain
|
||||
|
||||
txa[channel].bp1.p = create_bandpass (
|
||||
0, // ONLY RUNS WHEN COMPRESSOR IS USED
|
||||
0, // position
|
||||
ch[channel].dsp_size, // size
|
||||
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||
0, // flag for minimum phase
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
txa[channel].f_low, // low freq cutoff
|
||||
txa[channel].f_high, // high freq cutoff
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
1, // wintype
|
||||
2.0); // gain
|
||||
|
||||
txa[channel].osctrl.p = create_osctrl (
|
||||
0, // run
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
ch[channel].dsp_rate, // sample rate
|
||||
1.95); // gain for clippings
|
||||
|
||||
txa[channel].bp2.p = create_bandpass (
|
||||
0, // ONLY RUNS WHEN COMPRESSOR IS USED
|
||||
0, // position
|
||||
ch[channel].dsp_size, // size
|
||||
max(2048, ch[channel].dsp_size), // number of coefficients
|
||||
0, // flag for minimum phase
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
txa[channel].f_low, // low freq cutoff
|
||||
txa[channel].f_high, // high freq cutoff
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
1, // wintype
|
||||
1.0); // gain
|
||||
|
||||
txa[channel].compmeter.p = create_meter (
|
||||
1, // run
|
||||
&(txa[channel].compressor.p->run), // pointer to compressor 'run'
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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[channel].alc.p = create_wcpagc (
|
||||
1, // run - always ON
|
||||
5, // mode
|
||||
1, // 0 for max(I,Q), 1 for envelope
|
||||
txa[channel].midbuff, // input buff pointer
|
||||
txa[channel].midbuff, // output buff pointer
|
||||
ch[channel].dsp_size, // io_buffsize
|
||||
ch[channel].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[channel].ammod.p = create_ammod (
|
||||
0, // run - OFF by default
|
||||
0, // mode: 0=>AM, 1=>DSB
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to output buffer
|
||||
0.5); // carrier level
|
||||
|
||||
|
||||
txa[channel].fmmod.p = create_fmmod (
|
||||
0, // run - OFF by default
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
ch[channel].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
|
||||
max(2048, ch[channel].dsp_size), // number coefficients for bandpass filter
|
||||
0); // minimum phase flag
|
||||
|
||||
txa[channel].gen1.p = create_gen (
|
||||
0, // run
|
||||
ch[channel].dsp_size, // buffer size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
ch[channel].dsp_rate, // sample rate
|
||||
0); // mode
|
||||
|
||||
txa[channel].uslew.p = create_uslew (
|
||||
channel, // channel
|
||||
&ch[channel].iob.ch_upslew, // pointer to channel upslew flag
|
||||
ch[channel].dsp_size, // buffer size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
ch[channel].dsp_rate, // sample rate
|
||||
0.000, // delay time
|
||||
0.005); // upslew time
|
||||
|
||||
txa[channel].alcmeter.p = create_meter (
|
||||
1, // run
|
||||
0, // optional pointer to a 'run'
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // pointer to buffer
|
||||
ch[channel].dsp_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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[channel].alc.p->gain); // pointer for gain computation
|
||||
|
||||
txa[channel].sip1.p = create_siphon (
|
||||
1, // run
|
||||
0, // position
|
||||
0, // mode
|
||||
0, // disp
|
||||
ch[channel].dsp_size, // input buffer size
|
||||
txa[channel].midbuff, // input buffer
|
||||
16384, // number of samples to buffer
|
||||
16384, // fft size for spectrum
|
||||
1); // specmode
|
||||
|
||||
txa[channel].calcc.p = create_calcc (
|
||||
channel, // channel number
|
||||
1, // run calibration
|
||||
1024, // input buffer size
|
||||
ch[channel].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[channel].iqc.p0 = txa[channel].iqc.p1 = create_iqc (
|
||||
0, // run
|
||||
ch[channel].dsp_size, // size
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
(double)ch[channel].dsp_rate, // sample rate
|
||||
16, // ints
|
||||
0.005, // changeover time
|
||||
256); // spi
|
||||
|
||||
txa[channel].cfir.p = create_cfir(
|
||||
0, // run
|
||||
ch[channel].dsp_size, // size
|
||||
max(2048, ch[channel].dsp_size), // number of filter coefficients
|
||||
0, // minimum phase flag
|
||||
txa[channel].midbuff, // input buffer
|
||||
txa[channel].midbuff, // output buffer
|
||||
ch[channel].dsp_rate, // input sample rate
|
||||
ch[channel].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[channel].rsmpout.p = create_resample (
|
||||
0, // run - will be turned ON below if needed
|
||||
ch[channel].dsp_size, // input size
|
||||
txa[channel].midbuff, // pointer to input buffer
|
||||
txa[channel].outbuff, // pointer to output buffer
|
||||
ch[channel].dsp_rate, // input sample rate
|
||||
ch[channel].out_rate, // output sample rate
|
||||
0.0, // select cutoff automatically
|
||||
0, // select ncoef automatically
|
||||
0.980); // gain
|
||||
|
||||
txa[channel].outmeter.p = create_meter (
|
||||
1, // run
|
||||
0, // optional pointer to another 'run'
|
||||
ch[channel].dsp_outsize, // size
|
||||
txa[channel].outbuff, // pointer to buffer
|
||||
ch[channel].out_rate, // samplerate
|
||||
0.100, // averaging time constant
|
||||
0.100, // peak decay time constant
|
||||
txa[channel].meter, // result vector
|
||||
txa[channel].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
|
||||
TXAResCheck (channel);
|
||||
}
|
||||
|
||||
void destroy_txa (int channel)
|
||||
{
|
||||
// in reverse order, free each item we created
|
||||
destroy_meter (txa[channel].outmeter.p);
|
||||
destroy_resample (txa[channel].rsmpout.p);
|
||||
destroy_cfir(txa[channel].cfir.p);
|
||||
destroy_calcc (txa[channel].calcc.p);
|
||||
destroy_iqc (txa[channel].iqc.p0);
|
||||
destroy_siphon (txa[channel].sip1.p);
|
||||
destroy_meter (txa[channel].alcmeter.p);
|
||||
destroy_uslew (txa[channel].uslew.p);
|
||||
destroy_gen (txa[channel].gen1.p);
|
||||
destroy_fmmod (txa[channel].fmmod.p);
|
||||
destroy_ammod (txa[channel].ammod.p);
|
||||
destroy_wcpagc (txa[channel].alc.p);
|
||||
destroy_meter (txa[channel].compmeter.p);
|
||||
destroy_bandpass (txa[channel].bp2.p);
|
||||
destroy_osctrl (txa[channel].osctrl.p);
|
||||
destroy_bandpass (txa[channel].bp1.p);
|
||||
destroy_compressor (txa[channel].compressor.p);
|
||||
destroy_bandpass (txa[channel].bp0.p);
|
||||
destroy_meter (txa[channel].cfcmeter.p);
|
||||
destroy_cfcomp (txa[channel].cfcomp.p);
|
||||
destroy_meter (txa[channel].lvlrmeter.p);
|
||||
destroy_wcpagc (txa[channel].leveler.p);
|
||||
destroy_emphp (txa[channel].preemph.p);
|
||||
destroy_meter (txa[channel].eqmeter.p);
|
||||
destroy_eqp (txa[channel].eqp.p);
|
||||
destroy_amsq (txa[channel].amsq.p);
|
||||
destroy_meter (txa[channel].micmeter.p);
|
||||
destroy_phrot (txa[channel].phrot.p);
|
||||
destroy_panel (txa[channel].panel.p);
|
||||
destroy_gen (txa[channel].gen0.p);
|
||||
destroy_resample (txa[channel].rsmpin.p);
|
||||
_aligned_free (txa[channel].midbuff);
|
||||
_aligned_free (txa[channel].outbuff);
|
||||
_aligned_free (txa[channel].inbuff);
|
||||
}
|
||||
|
||||
void flush_txa (int channel)
|
||||
{
|
||||
memset (txa[channel].inbuff, 0, 1 * ch[channel].dsp_insize * sizeof (complex));
|
||||
memset (txa[channel].outbuff, 0, 1 * ch[channel].dsp_outsize * sizeof (complex));
|
||||
memset (txa[channel].midbuff, 0, 2 * ch[channel].dsp_size * sizeof (complex));
|
||||
flush_resample (txa[channel].rsmpin.p);
|
||||
flush_gen (txa[channel].gen0.p);
|
||||
flush_panel (txa[channel].panel.p);
|
||||
flush_phrot (txa[channel].phrot.p);
|
||||
flush_meter (txa[channel].micmeter.p);
|
||||
flush_amsq (txa[channel].amsq.p);
|
||||
flush_eqp (txa[channel].eqp.p);
|
||||
flush_meter (txa[channel].eqmeter.p);
|
||||
flush_emphp (txa[channel].preemph.p);
|
||||
flush_wcpagc (txa[channel].leveler.p);
|
||||
flush_meter (txa[channel].lvlrmeter.p);
|
||||
flush_cfcomp (txa[channel].cfcomp.p);
|
||||
flush_meter (txa[channel].cfcmeter.p);
|
||||
flush_bandpass (txa[channel].bp0.p);
|
||||
flush_compressor (txa[channel].compressor.p);
|
||||
flush_bandpass (txa[channel].bp1.p);
|
||||
flush_osctrl (txa[channel].osctrl.p);
|
||||
flush_bandpass (txa[channel].bp2.p);
|
||||
flush_meter (txa[channel].compmeter.p);
|
||||
flush_wcpagc (txa[channel].alc.p);
|
||||
flush_ammod (txa[channel].ammod.p);
|
||||
flush_fmmod (txa[channel].fmmod.p);
|
||||
flush_gen (txa[channel].gen1.p);
|
||||
flush_uslew (txa[channel].uslew.p);
|
||||
flush_meter (txa[channel].alcmeter.p);
|
||||
flush_siphon (txa[channel].sip1.p);
|
||||
flush_iqc (txa[channel].iqc.p0);
|
||||
flush_cfir(txa[channel].cfir.p);
|
||||
flush_resample (txa[channel].rsmpout.p);
|
||||
flush_meter (txa[channel].outmeter.p);
|
||||
}
|
||||
|
||||
void xtxa (int channel)
|
||||
{
|
||||
xresample (txa[channel].rsmpin.p); // input resampler
|
||||
xgen (txa[channel].gen0.p); // input signal generator
|
||||
xpanel (txa[channel].panel.p); // includes MIC gain
|
||||
xphrot (txa[channel].phrot.p); // phase rotator
|
||||
xmeter (txa[channel].micmeter.p); // MIC meter
|
||||
xamsqcap (txa[channel].amsq.p); // downward expander capture
|
||||
xamsq (txa[channel].amsq.p); // downward expander action
|
||||
xeqp (txa[channel].eqp.p); // pre-EQ
|
||||
xmeter (txa[channel].eqmeter.p); // EQ meter
|
||||
xemphp (txa[channel].preemph.p, 0); // FM pre-emphasis (first option)
|
||||
xwcpagc (txa[channel].leveler.p); // Leveler
|
||||
xmeter (txa[channel].lvlrmeter.p); // Leveler Meter
|
||||
xcfcomp (txa[channel].cfcomp.p, 0); // Continuous Frequency Compressor with post-EQ
|
||||
xmeter (txa[channel].cfcmeter.p); // CFC+PostEQ Meter
|
||||
xbandpass (txa[channel].bp0.p, 0); // primary bandpass filter
|
||||
xcompressor (txa[channel].compressor.p); // COMP compressor
|
||||
xbandpass (txa[channel].bp1.p, 0); // aux bandpass (runs if COMP)
|
||||
xosctrl (txa[channel].osctrl.p); // CESSB Overshoot Control
|
||||
xbandpass (txa[channel].bp2.p, 0); // aux bandpass (runs if CESSB)
|
||||
xmeter (txa[channel].compmeter.p); // COMP meter
|
||||
xwcpagc (txa[channel].alc.p); // ALC
|
||||
xammod (txa[channel].ammod.p); // AM Modulator
|
||||
xemphp (txa[channel].preemph.p, 1); // FM pre-emphasis (second option)
|
||||
xfmmod (txa[channel].fmmod.p); // FM Modulator
|
||||
xgen (txa[channel].gen1.p); // output signal generator (TUN and Two-tone)
|
||||
xuslew (txa[channel].uslew.p); // up-slew for AM, FM, and gens
|
||||
xmeter (txa[channel].alcmeter.p); // ALC Meter
|
||||
xsiphon (txa[channel].sip1.p, 0); // siphon data for display
|
||||
xiqc (txa[channel].iqc.p0); // PureSignal correction
|
||||
xcfir(txa[channel].cfir.p); // compensating FIR filter (used Protocol_2 only)
|
||||
xresample (txa[channel].rsmpout.p); // output resampler
|
||||
xmeter (txa[channel].outmeter.p); // output meter
|
||||
// print_peak_env ("env_exception.txt", ch[channel].dsp_outsize, txa[channel].outbuff, 0.7);
|
||||
}
|
||||
|
||||
void setInputSamplerate_txa (int channel)
|
||||
{
|
||||
// buffers
|
||||
_aligned_free (txa[channel].inbuff);
|
||||
txa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||
// input resampler
|
||||
setBuffers_resample (txa[channel].rsmpin.p, txa[channel].inbuff, txa[channel].midbuff);
|
||||
setSize_resample (txa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||
setInRate_resample (txa[channel].rsmpin.p, ch[channel].in_rate);
|
||||
TXAResCheck (channel);
|
||||
}
|
||||
|
||||
void setOutputSamplerate_txa (int channel)
|
||||
{
|
||||
// buffers
|
||||
_aligned_free (txa[channel].outbuff);
|
||||
txa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||
// cfir - needs to know input rate of firmware CIC
|
||||
setOutRate_cfir (txa[channel].cfir.p, ch[channel].out_rate);
|
||||
// output resampler
|
||||
setBuffers_resample (txa[channel].rsmpout.p, txa[channel].midbuff, txa[channel].outbuff);
|
||||
setOutRate_resample (txa[channel].rsmpout.p, ch[channel].out_rate);
|
||||
TXAResCheck (channel);
|
||||
// output meter
|
||||
setBuffers_meter (txa[channel].outmeter.p, txa[channel].outbuff);
|
||||
setSize_meter (txa[channel].outmeter.p, ch[channel].dsp_outsize);
|
||||
setSamplerate_meter (txa[channel].outmeter.p, ch[channel].out_rate);
|
||||
}
|
||||
|
||||
void setDSPSamplerate_txa (int channel)
|
||||
{
|
||||
// buffers
|
||||
_aligned_free (txa[channel].inbuff);
|
||||
txa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||
_aligned_free (txa[channel].outbuff);
|
||||
txa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||
// input resampler
|
||||
setBuffers_resample (txa[channel].rsmpin.p, txa[channel].inbuff, txa[channel].midbuff);
|
||||
setSize_resample (txa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||
setOutRate_resample (txa[channel].rsmpin.p, ch[channel].dsp_rate);
|
||||
// dsp_rate blocks
|
||||
setSamplerate_gen (txa[channel].gen0.p, ch[channel].dsp_rate);
|
||||
setSamplerate_panel (txa[channel].panel.p, ch[channel].dsp_rate);
|
||||
setSamplerate_phrot (txa[channel].phrot.p, ch[channel].dsp_rate);
|
||||
setSamplerate_meter (txa[channel].micmeter.p, ch[channel].dsp_rate);
|
||||
setSamplerate_amsq (txa[channel].amsq.p, ch[channel].dsp_rate);
|
||||
setSamplerate_eqp (txa[channel].eqp.p, ch[channel].dsp_rate);
|
||||
setSamplerate_meter (txa[channel].eqmeter.p, ch[channel].dsp_rate);
|
||||
setSamplerate_emphp (txa[channel].preemph.p, ch[channel].dsp_rate);
|
||||
setSamplerate_wcpagc (txa[channel].leveler.p, ch[channel].dsp_rate);
|
||||
setSamplerate_meter (txa[channel].lvlrmeter.p, ch[channel].dsp_rate);
|
||||
setSamplerate_cfcomp (txa[channel].cfcomp.p, ch[channel].dsp_rate);
|
||||
setSamplerate_meter (txa[channel].cfcmeter.p, ch[channel].dsp_rate);
|
||||
setSamplerate_bandpass (txa[channel].bp0.p, ch[channel].dsp_rate);
|
||||
setSamplerate_compressor (txa[channel].compressor.p, ch[channel].dsp_rate);
|
||||
setSamplerate_bandpass (txa[channel].bp1.p, ch[channel].dsp_rate);
|
||||
setSamplerate_osctrl (txa[channel].osctrl.p, ch[channel].dsp_rate);
|
||||
setSamplerate_bandpass (txa[channel].bp2.p, ch[channel].dsp_rate);
|
||||
setSamplerate_meter (txa[channel].compmeter.p, ch[channel].dsp_rate);
|
||||
setSamplerate_wcpagc (txa[channel].alc.p, ch[channel].dsp_rate);
|
||||
setSamplerate_ammod (txa[channel].ammod.p, ch[channel].dsp_rate);
|
||||
setSamplerate_fmmod (txa[channel].fmmod.p, ch[channel].dsp_rate);
|
||||
setSamplerate_gen (txa[channel].gen1.p, ch[channel].dsp_rate);
|
||||
setSamplerate_uslew (txa[channel].uslew.p, ch[channel].dsp_rate);
|
||||
setSamplerate_meter (txa[channel].alcmeter.p, ch[channel].dsp_rate);
|
||||
setSamplerate_siphon (txa[channel].sip1.p, ch[channel].dsp_rate);
|
||||
setSamplerate_iqc (txa[channel].iqc.p0, ch[channel].dsp_rate);
|
||||
setSamplerate_cfir (txa[channel].cfir.p, ch[channel].dsp_rate);
|
||||
// output resampler
|
||||
setBuffers_resample (txa[channel].rsmpout.p, txa[channel].midbuff, txa[channel].outbuff);
|
||||
setInRate_resample (txa[channel].rsmpout.p, ch[channel].dsp_rate);
|
||||
TXAResCheck (channel);
|
||||
// output meter
|
||||
setBuffers_meter (txa[channel].outmeter.p, txa[channel].outbuff);
|
||||
setSize_meter (txa[channel].outmeter.p, ch[channel].dsp_outsize);
|
||||
}
|
||||
|
||||
void setDSPBuffsize_txa (int channel)
|
||||
{
|
||||
// buffers
|
||||
_aligned_free (txa[channel].inbuff);
|
||||
txa[channel].inbuff = (double *)malloc0(1 * ch[channel].dsp_insize * sizeof(complex));
|
||||
_aligned_free (txa[channel].midbuff);
|
||||
txa[channel].midbuff = (double *)malloc0(2 * ch[channel].dsp_size * sizeof(complex));
|
||||
_aligned_free (txa[channel].outbuff);
|
||||
txa[channel].outbuff = (double *)malloc0(1 * ch[channel].dsp_outsize * sizeof(complex));
|
||||
// input resampler
|
||||
setBuffers_resample (txa[channel].rsmpin.p, txa[channel].inbuff, txa[channel].midbuff);
|
||||
setSize_resample (txa[channel].rsmpin.p, ch[channel].dsp_insize);
|
||||
// dsp_size blocks
|
||||
setBuffers_gen (txa[channel].gen0.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_gen (txa[channel].gen0.p, ch[channel].dsp_size);
|
||||
setBuffers_panel (txa[channel].panel.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_panel (txa[channel].panel.p, ch[channel].dsp_size);
|
||||
setBuffers_phrot (txa[channel].phrot.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_phrot (txa[channel].phrot.p, ch[channel].dsp_size);
|
||||
setBuffers_meter (txa[channel].micmeter.p, txa[channel].midbuff);
|
||||
setSize_meter (txa[channel].micmeter.p, ch[channel].dsp_size);
|
||||
setBuffers_amsq (txa[channel].amsq.p, txa[channel].midbuff, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_amsq (txa[channel].amsq.p, ch[channel].dsp_size);
|
||||
setBuffers_eqp (txa[channel].eqp.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_eqp (txa[channel].eqp.p, ch[channel].dsp_size);
|
||||
setBuffers_meter (txa[channel].eqmeter.p, txa[channel].midbuff);
|
||||
setSize_meter (txa[channel].eqmeter.p, ch[channel].dsp_size);
|
||||
setBuffers_emphp (txa[channel].preemph.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_emphp (txa[channel].preemph.p, ch[channel].dsp_size);
|
||||
setBuffers_wcpagc (txa[channel].leveler.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_wcpagc (txa[channel].leveler.p, ch[channel].dsp_size);
|
||||
setBuffers_meter (txa[channel].lvlrmeter.p, txa[channel].midbuff);
|
||||
setSize_meter (txa[channel].lvlrmeter.p, ch[channel].dsp_size);
|
||||
setBuffers_cfcomp (txa[channel].cfcomp.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_cfcomp (txa[channel].cfcomp.p, ch[channel].dsp_size);
|
||||
setBuffers_meter (txa[channel].cfcmeter.p, txa[channel].midbuff);
|
||||
setSize_meter (txa[channel].cfcmeter.p, ch[channel].dsp_size);
|
||||
setBuffers_bandpass (txa[channel].bp0.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_bandpass (txa[channel].bp0.p, ch[channel].dsp_size);
|
||||
setBuffers_compressor (txa[channel].compressor.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_compressor (txa[channel].compressor.p, ch[channel].dsp_size);
|
||||
setBuffers_bandpass (txa[channel].bp1.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_bandpass (txa[channel].bp1.p, ch[channel].dsp_size);
|
||||
setBuffers_osctrl (txa[channel].osctrl.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_osctrl (txa[channel].osctrl.p, ch[channel].dsp_size);
|
||||
setBuffers_bandpass (txa[channel].bp2.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_bandpass (txa[channel].bp2.p, ch[channel].dsp_size);
|
||||
setBuffers_meter (txa[channel].compmeter.p, txa[channel].midbuff);
|
||||
setSize_meter (txa[channel].compmeter.p, ch[channel].dsp_size);
|
||||
setBuffers_wcpagc (txa[channel].alc.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_wcpagc (txa[channel].alc.p, ch[channel].dsp_size);
|
||||
setBuffers_ammod (txa[channel].ammod.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_ammod (txa[channel].ammod.p, ch[channel].dsp_size);
|
||||
setBuffers_fmmod (txa[channel].fmmod.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_fmmod (txa[channel].fmmod.p, ch[channel].dsp_size);
|
||||
setBuffers_gen (txa[channel].gen1.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_gen (txa[channel].gen1.p, ch[channel].dsp_size);
|
||||
setBuffers_uslew (txa[channel].uslew.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_uslew (txa[channel].uslew.p, ch[channel].dsp_size);
|
||||
setBuffers_meter (txa[channel].alcmeter.p, txa[channel].midbuff);
|
||||
setSize_meter (txa[channel].alcmeter.p, ch[channel].dsp_size);
|
||||
setBuffers_siphon (txa[channel].sip1.p, txa[channel].midbuff);
|
||||
setSize_siphon (txa[channel].sip1.p, ch[channel].dsp_size);
|
||||
setBuffers_iqc (txa[channel].iqc.p0, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_iqc (txa[channel].iqc.p0, ch[channel].dsp_size);
|
||||
setBuffers_cfir (txa[channel].cfir.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||
setSize_cfir (txa[channel].cfir.p, ch[channel].dsp_size);
|
||||
// output resampler
|
||||
setBuffers_resample (txa[channel].rsmpout.p, txa[channel].midbuff, txa[channel].outbuff);
|
||||
setSize_resample (txa[channel].rsmpout.p, ch[channel].dsp_size);
|
||||
// output meter
|
||||
setBuffers_meter (txa[channel].outmeter.p, txa[channel].outbuff);
|
||||
setSize_meter (txa[channel].outmeter.p, ch[channel].dsp_outsize);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXAMode (int channel, int mode)
|
||||
{
|
||||
if (txa[channel].mode != mode)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].mode = mode;
|
||||
txa[channel].ammod.p->run = 0;
|
||||
txa[channel].fmmod.p->run = 0;
|
||||
txa[channel].preemph.p->run = 0;
|
||||
switch (mode)
|
||||
{
|
||||
case TXA_AM:
|
||||
case TXA_SAM:
|
||||
txa[channel].ammod.p->run = 1;
|
||||
txa[channel].ammod.p->mode = 0;
|
||||
break;
|
||||
case TXA_DSB:
|
||||
txa[channel].ammod.p->run = 1;
|
||||
txa[channel].ammod.p->mode = 1;
|
||||
break;
|
||||
case TXA_AM_LSB:
|
||||
case TXA_AM_USB:
|
||||
txa[channel].ammod.p->run = 1;
|
||||
txa[channel].ammod.p->mode = 2;
|
||||
break;
|
||||
case TXA_FM:
|
||||
txa[channel].fmmod.p->run = 1;
|
||||
txa[channel].preemph.p->run = 1;
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
TXASetupBPFilters (channel);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXABandpassFreqs (int channel, double f_low, double f_high)
|
||||
{
|
||||
if ((txa[channel].f_low != f_low) || (txa[channel].f_high != f_high))
|
||||
{
|
||||
txa[channel].f_low = f_low;
|
||||
txa[channel].f_high = f_high;
|
||||
TXASetupBPFilters (channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Internal Functions *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void TXAResCheck (int channel)
|
||||
{
|
||||
RESAMPLE a = txa[channel].rsmpin.p;
|
||||
if (ch[channel].in_rate != ch[channel].dsp_rate) a->run = 1;
|
||||
else a->run = 0;
|
||||
a = txa[channel].rsmpout.p;
|
||||
if (ch[channel].dsp_rate != ch[channel].out_rate) a->run = 1;
|
||||
else a->run = 0;
|
||||
}
|
||||
|
||||
int TXAUslewCheck (int channel)
|
||||
{
|
||||
return (txa[channel].ammod.p->run == 1) ||
|
||||
(txa[channel].fmmod.p->run == 1) ||
|
||||
(txa[channel].gen0.p->run == 1) ||
|
||||
(txa[channel].gen1.p->run == 1);
|
||||
}
|
||||
|
||||
void TXASetupBPFilters (int channel)
|
||||
{
|
||||
txa[channel].bp0.p->run = 1;
|
||||
txa[channel].bp1.p->run = 0;
|
||||
txa[channel].bp2.p->run = 0;
|
||||
switch (txa[channel].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:
|
||||
CalcBandpassFilter (txa[channel].bp0.p, txa[channel].f_low, txa[channel].f_high, 2.0);
|
||||
if (txa[channel].compressor.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp1.p, txa[channel].f_low, txa[channel].f_high, 2.0);
|
||||
txa[channel].bp1.p->run = 1;
|
||||
if (txa[channel].osctrl.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp2.p, txa[channel].f_low, txa[channel].f_high, 1.0);
|
||||
txa[channel].bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TXA_DSB:
|
||||
case TXA_AM:
|
||||
case TXA_SAM:
|
||||
case TXA_FM:
|
||||
if (txa[channel].compressor.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp0.p, 0.0, txa[channel].f_high, 2.0);
|
||||
CalcBandpassFilter (txa[channel].bp1.p, 0.0, txa[channel].f_high, 2.0);
|
||||
txa[channel].bp1.p->run = 1;
|
||||
if (txa[channel].osctrl.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp2.p, 0.0, txa[channel].f_high, 1.0);
|
||||
txa[channel].bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp0.p, txa[channel].f_low, txa[channel].f_high, 1.0);
|
||||
}
|
||||
break;
|
||||
case TXA_AM_LSB:
|
||||
CalcBandpassFilter (txa[channel].bp0.p, -txa[channel].f_high, 0.0, 2.0);
|
||||
if (txa[channel].compressor.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp1.p, -txa[channel].f_high, 0.0, 2.0);
|
||||
txa[channel].bp1.p->run = 1;
|
||||
if (txa[channel].osctrl.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp2.p, -txa[channel].f_high, 0.0, 1.0);
|
||||
txa[channel].bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TXA_AM_USB:
|
||||
CalcBandpassFilter (txa[channel].bp0.p, 0.0, txa[channel].f_high, 2.0);
|
||||
if (txa[channel].compressor.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp1.p, 0.0, txa[channel].f_high, 2.0);
|
||||
txa[channel].bp1.p->run = 1;
|
||||
if (txa[channel].osctrl.p->run)
|
||||
{
|
||||
CalcBandpassFilter (txa[channel].bp2.p, 0.0, txa[channel].f_high, 1.0);
|
||||
txa[channel].bp2.p->run = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Collectives *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void TXASetNC (int channel, int nc)
|
||||
{
|
||||
int oldstate = SetChannelState (channel, 0, 1);
|
||||
SetTXABandpassNC (channel, nc);
|
||||
SetTXAFMEmphNC (channel, nc);
|
||||
SetTXAEQNC (channel, nc);
|
||||
SetTXAFMNC (channel, nc);
|
||||
SetTXACFIRNC (channel, nc);
|
||||
SetChannelState (channel, oldstate, 0);
|
||||
}
|
||||
|
||||
PORT
|
||||
void TXASetMP (int channel, int mp)
|
||||
{
|
||||
SetTXABandpassMP (channel, mp);
|
||||
SetTXAFMEmphMP (channel, mp);
|
||||
SetTXAEQMP (channel, mp);
|
||||
SetTXAFMMP (channel, mp);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMAFFilter (int channel, double low, double high)
|
||||
{
|
||||
SetTXAFMPreEmphFreqs (channel, low, high);
|
||||
SetTXAFMAFFreqs (channel, low, high);
|
||||
}
|
||||
193
TXA.h
Normal file
193
TXA.h
Normal file
@ -0,0 +1,193 @@
|
||||
/* TXA.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2017 Warren Pratt, NR0V
|
||||
|
||||
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 _txa_h
|
||||
#define _txa_h
|
||||
#include "comm.h"
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct _txa
|
||||
{
|
||||
double* inbuff;
|
||||
double* outbuff;
|
||||
double* midbuff;
|
||||
int mode;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double meter[TXA_METERTYPE_LAST];
|
||||
CRITICAL_SECTION* pmtupdate[TXA_METERTYPE_LAST];
|
||||
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
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
extern struct _txa txa[];
|
||||
|
||||
extern void create_txa (int channel);
|
||||
|
||||
extern void destroy_txa (int channel);
|
||||
|
||||
extern void flush_txa (int channel);
|
||||
|
||||
extern void xtxa (int channel);
|
||||
|
||||
extern int TXAUslewCheck (int channel);
|
||||
|
||||
extern void setInputSamplerate_txa (int channel);
|
||||
|
||||
extern void setOutputSamplerate_txa (int channel);
|
||||
|
||||
extern void setDSPSamplerate_txa (int channel);
|
||||
|
||||
extern void setDSPBuffsize_txa (int channel);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetTXAMode (int channel, int mode);
|
||||
|
||||
extern void TXAResCheck (int channel);
|
||||
|
||||
extern void TXASetupBPFilters (int channel);
|
||||
|
||||
#endif
|
||||
294
amd.c
Normal file
294
amd.c
Normal file
@ -0,0 +1,294 @@
|
||||
/* amd.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
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 = (AMD) malloc0 (sizeof(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 destroy_amd(AMD a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void init_amd(AMD a)
|
||||
{
|
||||
//pll
|
||||
a->omega_min = TWOPI * a->fmin / a->sample_rate;
|
||||
a->omega_max = TWOPI * a->fmax / a->sample_rate;
|
||||
a->g1 = 1.0 - 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 flush_amd (AMD a)
|
||||
{
|
||||
a->dc = 0.0;
|
||||
a->dc_insert = 0.0;
|
||||
}
|
||||
|
||||
void 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 >= TWOPI) a->phs -= TWOPI;
|
||||
while (a->phs < 0.0) a->phs += TWOPI;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->in_buff != a->out_buff)
|
||||
memcpy (a->out_buff, a->in_buff, a->buff_size * sizeof(complex));
|
||||
}
|
||||
|
||||
void setBuffers_amd (AMD a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void setSamplerate_amd (AMD a, int rate)
|
||||
{
|
||||
a->sample_rate = rate;
|
||||
init_amd(a);
|
||||
}
|
||||
|
||||
void setSize_amd (AMD a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT void
|
||||
SetRXAAMDRun(int channel, int run)
|
||||
{
|
||||
AMD a = rxa[channel].amd.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
RXAbp1Check (channel, run, rxa[channel].snba.p->run, rxa[channel].emnr.p->run,
|
||||
rxa[channel].anf.p->run, rxa[channel].anr.p->run,
|
||||
rxa[channel].rnnr.p->run, rxa[channel].sbnr.p->run); // NR3 + NR4 support
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->run = run;
|
||||
RXAbp1Set (channel);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAAMDSBMode(int channel, int sbmode)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].amd.p->sbmode = sbmode;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAAMDFadeLevel(int channel, int levelfade)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].amd.p->levelfade = levelfade;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
118
amd.h
Normal file
118
amd.h
Normal file
@ -0,0 +1,118 @@
|
||||
/* amd.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _amd_h
|
||||
#define _amd_h
|
||||
|
||||
// ff defines for sbdemod
|
||||
#ifndef STAGES
|
||||
#define STAGES 7
|
||||
#endif
|
||||
|
||||
#ifndef OUT_IDX
|
||||
#define OUT_IDX (3 * STAGES)
|
||||
#endif
|
||||
|
||||
typedef struct _amd
|
||||
{
|
||||
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
|
||||
|
||||
}amd, *AMD;
|
||||
|
||||
extern 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
|
||||
);
|
||||
|
||||
extern void init_amd (AMD a);
|
||||
|
||||
extern void destroy_amd (AMD a);
|
||||
|
||||
extern void flush_amd (AMD a);
|
||||
|
||||
extern void xamd (AMD a);
|
||||
|
||||
extern void setBuffers_amd (AMD a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_amd (AMD a, int rate);
|
||||
|
||||
extern void setSize_amd (AMD a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAAMDRun(int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAAMDSBMode(int channel, int sbmode);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAAMDFadeLevel(int channel, int levelfade);
|
||||
|
||||
#endif
|
||||
110
ammod.c
Normal file
110
ammod.c
Normal file
@ -0,0 +1,110 @@
|
||||
/* ammod.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2017 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
AMMOD create_ammod (int run, int mode, int size, double* in, double* out, double c_level)
|
||||
{
|
||||
AMMOD a = (AMMOD) malloc0 (sizeof (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 destroy_ammod (AMMOD a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_ammod (AMMOD a)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_ammod (AMMOD a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void setSamplerate_ammod (AMMOD a, int rate)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void setSize_ammod (AMMOD a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT void
|
||||
SetTXAAMCarrierLevel (int channel, double c_level)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].ammod.p->c_level = c_level;
|
||||
txa[channel].ammod.p->a_level = 1.0 - c_level;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
60
ammod.h
Normal file
60
ammod.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* ammod.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _ammod_h
|
||||
#define _ammod_h
|
||||
|
||||
typedef struct _ammod
|
||||
{
|
||||
int run;
|
||||
int mode;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double c_level;
|
||||
double a_level;
|
||||
double mult;
|
||||
}ammod, *AMMOD;
|
||||
|
||||
extern AMMOD create_ammod (int run, int mode, int size, double* in, double* out, double c_level);
|
||||
|
||||
extern void destroy_ammod (AMMOD a);
|
||||
|
||||
extern void flush_ammod (AMMOD a);
|
||||
|
||||
extern void xammod (AMMOD a);
|
||||
|
||||
extern void setBuffers_ammod (AMMOD a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_ammod (AMMOD a, int rate);
|
||||
|
||||
extern void setSize_ammod (AMMOD a, int size);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetTXAAMCarrierLevel (int channel, double c_level);
|
||||
|
||||
#endif
|
||||
272
amsq.c
Normal file
272
amsq.c
Normal file
@ -0,0 +1,272 @@
|
||||
/* amsq.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 calc_amsq(AMSQ a)
|
||||
{
|
||||
// signal averaging
|
||||
a->trigsig = (double *)malloc0(a->size * sizeof(complex));
|
||||
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 = (double *)malloc0((a->ntup + 1) * sizeof(double));
|
||||
a->cdown = (double *)malloc0((a->ntdown + 1) * sizeof(double));
|
||||
compute_slews(a);
|
||||
// control
|
||||
a->state = 0;
|
||||
}
|
||||
|
||||
void decalc_amsq (AMSQ a)
|
||||
{
|
||||
_aligned_free (a->cdown);
|
||||
_aligned_free (a->cup);
|
||||
_aligned_free (a->trigsig);
|
||||
}
|
||||
|
||||
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 = (AMSQ) malloc0 (sizeof (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 destroy_amsq (AMSQ a)
|
||||
{
|
||||
decalc_amsq (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_amsq (AMSQ a)
|
||||
{
|
||||
memset (a->trigsig, 0, a->size * sizeof (complex));
|
||||
a->avsig = 0.0;
|
||||
a->state = 0;
|
||||
}
|
||||
|
||||
enum _amsqstate
|
||||
{
|
||||
MUTED,
|
||||
INCREASE,
|
||||
UNMUTED,
|
||||
TAIL,
|
||||
DECREASE
|
||||
};
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void xamsqcap (AMSQ a)
|
||||
{
|
||||
memcpy (a->trigsig, a->trigger, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_amsq (AMSQ a, double* in, double* out, double* trigger)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->trigger = trigger;
|
||||
}
|
||||
|
||||
void setSamplerate_amsq (AMSQ a, int rate)
|
||||
{
|
||||
decalc_amsq (a);
|
||||
a->rate = rate;
|
||||
calc_amsq (a);
|
||||
}
|
||||
|
||||
void setSize_amsq (AMSQ a, int size)
|
||||
{
|
||||
decalc_amsq (a);
|
||||
a->size = size;
|
||||
calc_amsq (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXAAMSQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].amsq.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAAMSQThreshold (int channel, double threshold)
|
||||
{
|
||||
double thresh = pow (10.0, threshold / 20.0);
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].amsq.p->tail_thresh = 0.9 * thresh;
|
||||
rxa[channel].amsq.p->unmute_thresh = thresh;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAAMSQMaxTail (int channel, double tail)
|
||||
{
|
||||
AMSQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].amsq.p;
|
||||
if (tail < a->min_tail) tail = a->min_tail;
|
||||
a->max_tail = tail;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXAAMSQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].amsq.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAAMSQMutedGain (int channel, double dBlevel)
|
||||
{ // dBlevel is negative
|
||||
AMSQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].amsq.p;
|
||||
a->muted_gain = pow (10.0, dBlevel / 20.0);
|
||||
compute_slews(a);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAAMSQThreshold (int channel, double threshold)
|
||||
{
|
||||
double thresh = pow (10.0, threshold / 20.0);
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].amsq.p->tail_thresh = 0.9 * thresh;
|
||||
txa[channel].amsq.p->unmute_thresh = thresh;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
89
amsq.h
Normal file
89
amsq.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* amsq.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
|
||||
typedef struct _amsq
|
||||
{
|
||||
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;
|
||||
} amsq, *AMSQ;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_amsq (AMSQ a);
|
||||
|
||||
extern void flush_amsq (AMSQ a);
|
||||
|
||||
extern void xamsq (AMSQ a);
|
||||
|
||||
extern void xamsqcap (AMSQ a);
|
||||
|
||||
extern void setBuffers_amsq (AMSQ a, double* in, double* out, double* trigger);
|
||||
|
||||
extern void setSamplerate_amsq (AMSQ a, int rate);
|
||||
|
||||
extern void setSize_amsq (AMSQ a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAAMSQRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAAMSQThreshold (int channel, double threshold);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAAMSQMaxTail (int channel, double tail);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetTXAAMSQRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAAMSQMutedGain (int channel, double dBlevel);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAAMSQThreshold (int channel, double threshold);
|
||||
|
||||
#endif
|
||||
1882
analyzer.c
Normal file
1882
analyzer.c
Normal file
File diff suppressed because it is too large
Load Diff
215
analyzer.h
Normal file
215
analyzer.h
Normal file
@ -0,0 +1,215 @@
|
||||
/* analyzer.h
|
||||
|
||||
This file is part of a program that implements a Spectrum Analyzer
|
||||
used in conjunction with software-defined-radio hardware.
|
||||
|
||||
Copyright (C) 2012, 2013, 2014, 2016, 2023, 2025 Warren Pratt, NR0V
|
||||
|
||||
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 _analyzer_h
|
||||
#define _analyzer_h
|
||||
#include "comm.h"
|
||||
|
||||
typedef struct _dp
|
||||
{
|
||||
int max_size; // maximum fft size to be used
|
||||
int max_num_fft; // maximum number of LO positions per sub-span to be used
|
||||
int max_stitch; // maximum number of sub-spans to be concatenated
|
||||
// NOTE: max_size, max_num_fft, and max_stitch MUST BE <= THE
|
||||
// CORRESPONDING VALUES IN <analyzer.h>!!
|
||||
int num_fft; // current number of ffts in use
|
||||
int num_pixout; // current number of detector/averages/pixel value outputs
|
||||
int size; // current size of fft input sample vector
|
||||
int out_size; // current size of fft output vector
|
||||
int window_type; // type of the window function to be applied
|
||||
int overlap; // number of samples re-used per fft, range 0 to size-1
|
||||
int flip[dMAX_NUM_FFT]; // 0 for low-side LO => do NOT flip; 1 for high-side LO => FLIP
|
||||
int clip; // number of bins to clip off on EACH end of the sub-span fft
|
||||
// ASSUMES size/2 IS AN EVEN NUMBER!!!
|
||||
double fsclipL; // number of intervals to clip off the lower end of the TOTAL SPAN
|
||||
double fsclipH; // number of intervals to clip off the upper end of the TOTAL SPAN
|
||||
int fscL; // fsclipL modulo (out_size - 2 * clip)
|
||||
int fscH; // fsclipH modulo (out_size - 2 * clip)
|
||||
int begin_ss; // number of first sub-span that is NOT completely clipped off
|
||||
int end_ss; // number of last sub-span that is NOT completely clipped off
|
||||
int ss_bins[dMAX_STITCH]; // number of bins delivered by eliminate()/Celiminate in each sub-span
|
||||
volatile LONG input_busy[dMAX_STITCH][dMAX_NUM_FFT];
|
||||
int num_pixels; // number of pixels requested
|
||||
int num_stitch; // number of results to be stitched together to generate the pixel frame
|
||||
unsigned long long stitch_flag;
|
||||
int spec_flag[dMAX_STITCH]; // flags showing if all ffts for a sub-span are done so elimination can proceed
|
||||
double pix_per_bin; // number of pixels per fft bin, note that this is fractional, not integral
|
||||
double det_offset; // offset needed in detector
|
||||
double bin_per_pix; // number of fft bins per pixel, this is fractional and != 1.0/pix_per_bin
|
||||
double scale; // output amplitude scale factor
|
||||
double PiAlpha; // parameter for Kaiser window function
|
||||
|
||||
int cal_set; // specifies which set of calibration data to use
|
||||
double f_min; // frequency at first pixel (for calibration)
|
||||
double f_max; // frequency at last pixel (for calibration)
|
||||
int cal_changed; // flag to indicate that the calibration data has changed
|
||||
|
||||
double *window; // pointer to buffer to hold window coefficients
|
||||
double *result[dMAX_STITCH]; // pointers to buffer to hold elimination results for each sub-span
|
||||
dOUTREAL *pixels[dMAX_PIXOUTS][dNUM_PIXEL_BUFFS]; // pointers pixel output buffers
|
||||
double *t_pixels[dMAX_PIXOUTS]; // pointer to temporary pixel buffer //pointer to temporary pixel buffer for non-averaged data
|
||||
int w_pix_buff[dMAX_PIXOUTS]; // number of pixel buffer owned by writing process
|
||||
int r_pix_buff[dMAX_PIXOUTS]; // number of pixel buffer owned by reading process
|
||||
int last_pix_buff[dMAX_PIXOUTS]; // number of the last pixel buffer written
|
||||
volatile LONG pb_ready[dMAX_PIXOUTS][dNUM_PIXEL_BUFFS]; // if value is 0, this data has already been read; 1 = fresh data to read
|
||||
int num_average[dMAX_PIXOUTS]; // number of spans to average to create the pixels
|
||||
int avail_frames[dMAX_PIXOUTS]; // number of pixel frames currently available to average
|
||||
int av_in_idx[dMAX_PIXOUTS]; // input index in averaging pixel buffer ring
|
||||
int av_out_idx[dMAX_PIXOUTS]; // output index in averaging pixel buffer ring
|
||||
double *av_sum[dMAX_PIXOUTS]; // pointer to sum buffer for averaging
|
||||
double *av_buff[dMAX_PIXOUTS][dMAX_AVERAGE]; // pointers to ring of buffers to hold pixel frames for averaging
|
||||
double *pre_av_out;
|
||||
int av_mode[dMAX_PIXOUTS];
|
||||
double av_backmult[dMAX_PIXOUTS]; // back multiplier for weighted averaging
|
||||
double *cd; // pointer to amplitude calibration buffer
|
||||
int n_freqs[dMAX_CAL_SETS]; // number of frequencies in each calibration set
|
||||
double *freqs[dMAX_CAL_SETS]; // pointers to vectors of calibration frequencies
|
||||
double (*ac3[dMAX_CAL_SETS][dMAX_M]); // pointers to amplitude interpolant coefficients
|
||||
double (*ac2[dMAX_CAL_SETS][dMAX_M]);
|
||||
double (*ac1[dMAX_CAL_SETS][dMAX_M]);
|
||||
double (*ac0[dMAX_CAL_SETS][dMAX_M]);
|
||||
|
||||
fftw_plan plan[dMAX_STITCH][dMAX_NUM_FFT]; // fftw plans
|
||||
fftw_plan Cplan[dMAX_STITCH][dMAX_NUM_FFT];
|
||||
double *fft_in[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to fftw real input vectors
|
||||
fftw_complex *Cfft_in[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to fftw complex input vectors
|
||||
fftw_complex *fft_out[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to fftw complex output vectors
|
||||
volatile LONG *pnum_threads; // pointer to current number of active worker threads
|
||||
int stop; // when set, fft threads will be returned to the pool
|
||||
int end_dispatcher; // set this flag to one to destroy the dispatcher thread
|
||||
volatile int dispatcher; // one if the dispatcher thread is alive & active
|
||||
int ss; // sub-span being processed
|
||||
int LO; // LO (within current sub-span) being processed
|
||||
int flag;
|
||||
int have_samples[dMAX_STITCH][dMAX_NUM_FFT]; // number of unused samples remaining in a buffer
|
||||
int type; // 0 for REAL, 1 for COMPLEX
|
||||
int incr; // size - overlap
|
||||
int buff_size; // amount of data to be stored each time an input buffer is opened and closed = JanusAudio/BlockSize
|
||||
dINREAL* I_samples[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to current input position in I/Q buffers
|
||||
dINREAL* Q_samples[dMAX_STITCH][dMAX_NUM_FFT];
|
||||
int bsize; // size of I_samples[][] and Q_samples[][] (number of samples they hold)
|
||||
int IQout_index[dMAX_STITCH][dMAX_NUM_FFT]; // current output index for I_samples[ss][LO] and Q_samples[ss][LO]
|
||||
int IQO_idx[dMAX_STITCH][dMAX_NUM_FFT];
|
||||
int IQin_index[dMAX_STITCH][dMAX_NUM_FFT]; // current input index for I_samples[ss][LO] and Q_samples[ss][LO]
|
||||
volatile LONG buff_ready[dMAX_STITCH][dMAX_NUM_FFT]; // 1 if buffer ready to read; 0 if needs to be filled
|
||||
int max_writeahead; // max allowed input samples ahead of where reading output samples
|
||||
|
||||
volatile LONG snap[dMAX_STITCH][dMAX_NUM_FFT]; // set to 1 to allow a snap of raw spectrum data
|
||||
HANDLE hSnapEvent[dMAX_STITCH][dMAX_NUM_FFT]; // mutex handles; mutexes will be used to signal a snap is complete
|
||||
double *snap_buff[dMAX_STITCH][dMAX_NUM_FFT]; // pointers to buffers for the snap
|
||||
|
||||
CRITICAL_SECTION PB_ControlsSection[dMAX_PIXOUTS];
|
||||
CRITICAL_SECTION SetAnalyzerSection;
|
||||
CRITICAL_SECTION BufferControlSection[dMAX_STITCH][dMAX_NUM_FFT];
|
||||
CRITICAL_SECTION StitchSection;
|
||||
CRITICAL_SECTION EliminateSection[dMAX_STITCH];
|
||||
CRITICAL_SECTION ResampleSection;
|
||||
|
||||
int det_type[dMAX_PIXOUTS]; // detector type
|
||||
double inv_coherent_gain;
|
||||
double inherent_power_gain;
|
||||
double inv_enb;
|
||||
double norm_oneHz; // dB factor to normalize to one Hz bandwidth
|
||||
int sample_rate; // sample rate; used for normalization calculations
|
||||
int normalize[dMAX_PIXOUTS];
|
||||
|
||||
// BEGIN CODE TO GET MAX FFT_BIN WITHIN A FREQUENCY RANGE
|
||||
int dmb_run;
|
||||
int dmb_disp;
|
||||
int dmb_ss;
|
||||
int dmb_LO;
|
||||
double dmb_rate;
|
||||
double dmb_fLow;
|
||||
double dmb_fHigh;
|
||||
double dmb_tau;
|
||||
int dmb_frame_rate;
|
||||
int dmb_begin0;
|
||||
int dmb_end0;
|
||||
int dmb_begin1;
|
||||
int dmb_end1;
|
||||
double dmb_decay;
|
||||
double dmb_max_dB;
|
||||
CRITICAL_SECTION cs_dmb;
|
||||
// END CODE TO GET MAX FFT_BIN WITHIN A FREQUENCY RANGE
|
||||
|
||||
} dp, *DP;
|
||||
|
||||
extern DP pdisp[];
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void CreateAnalyzer ( int disp,
|
||||
int *success,
|
||||
char *app_data_path);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void XCreateAnalyzer ( int disp,
|
||||
int *success, //writes '0' to success if all went well, <0 if mem alloc failed
|
||||
int m_size, //maximum fft size to be used
|
||||
int m_LO, //maximum number of LO positions per subspan
|
||||
int m_stitch, //maximum number of subspans to be concatenated
|
||||
char *app_data_path
|
||||
);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void DestroyAnalyzer(int disp);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void SetCalibration ( int disp,
|
||||
int set_num, //identifier for this calibration data set
|
||||
int n_points, //number of calibration points in the set
|
||||
double (*cal)[dMAX_M+1] //pointer to the calibration table, first
|
||||
);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void OpenBuffer(int disp, int ss, int LO, void **Ipointer, void **Qpointer);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void CloseBuffer(int disp, int ss, int LO);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void Spectrum(int disp, int ss, int LO, dINREAL* pI, dINREAL* pQ);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void Spectrum2(int run, int disp, int ss, int LO, dINREAL* pbuff);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void Spectrum0(int run, int disp, int ss, int LO, double* pbuff);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void SnapSpectrum( int disp,
|
||||
int ss,
|
||||
int LO,
|
||||
double *snap_buff);
|
||||
|
||||
extern __declspec( dllexport )
|
||||
void SnapSpectrumTimeout (int disp,
|
||||
int ss,
|
||||
int LO,
|
||||
double* snap_buff,
|
||||
DWORD timeout,
|
||||
int* flag);
|
||||
|
||||
#endif
|
||||
240
anf.c
Normal file
240
anf.c
Normal file
@ -0,0 +1,240 @@
|
||||
/* anf.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
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 = (ANF) malloc0 (sizeof(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 destroy_anf (ANF a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void 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 setBuffers_anf (ANF a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void setSamplerate_anf (ANF a, int rate)
|
||||
{
|
||||
flush_anf (a);
|
||||
}
|
||||
|
||||
void setSize_anf (ANF a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
flush_anf (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT void
|
||||
SetRXAANFRun (int channel, int run)
|
||||
{
|
||||
ANF a = rxa[channel].anf.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
RXAbp1Check (channel, rxa[channel].amd.p->run, rxa[channel].snba.p->run,
|
||||
rxa[channel].emnr.p->run, run, rxa[channel].anr.p->run,
|
||||
rxa[channel].rnnr.p->run, rxa[channel].sbnr.p->run); // NR3 + NR4 support
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->run = run;
|
||||
RXAbp1Set (channel);
|
||||
flush_anf (a);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PORT void
|
||||
SetRXAANFVals (int channel, int taps, int delay, double gain, double leakage)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anf.p->n_taps = taps;
|
||||
rxa[channel].anf.p->delay = delay;
|
||||
rxa[channel].anf.p->two_mu = gain; //try two_mu = 1e-4
|
||||
rxa[channel].anf.p->gamma = leakage; //try gamma = 0.10
|
||||
flush_anf (rxa[channel].anf.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANFTaps (int channel, int taps)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anf.p->n_taps = taps;
|
||||
flush_anf (rxa[channel].anf.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANFDelay (int channel, int delay)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anf.p->delay = delay;
|
||||
flush_anf (rxa[channel].anf.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANFGain (int channel, double gain)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anf.p->two_mu = gain;
|
||||
flush_anf (rxa[channel].anf.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANFLeakage (int channel, double leakage)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anf.p->gamma = leakage;
|
||||
flush_anf (rxa[channel].anf.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANFPosition (int channel, int position)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anf.p->position = position;
|
||||
rxa[channel].bp1.p->position = position;
|
||||
flush_anf (rxa[channel].anf.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
107
anf.h
Normal file
107
anf.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* anf.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _anf_h
|
||||
#define _anf_h
|
||||
|
||||
#define ANF_DLINE_SIZE 2048
|
||||
|
||||
typedef struct _anf
|
||||
{
|
||||
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;
|
||||
} anf, *ANF;
|
||||
|
||||
extern 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
|
||||
);
|
||||
|
||||
extern void destroy_anf (ANF a);
|
||||
|
||||
extern void flush_anf (ANF a);
|
||||
|
||||
extern void xanf (ANF a, int position);
|
||||
|
||||
extern void setBuffers_anf (ANF a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_anf (ANF a, int rate);
|
||||
|
||||
extern void setSize_anf (ANF a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFRun (int channel, int setit);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFVals (int channel, int taps, int delay, double gain, double leakage);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFTaps (int channel, int taps);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFDelay (int channel, int delay);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFGain (int channel, double gain);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFLeakage (int channel, double leakage);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANFPosition (int channel, int position);
|
||||
|
||||
#endif
|
||||
239
anr.c
Normal file
239
anr.c
Normal file
@ -0,0 +1,239 @@
|
||||
/* anr.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
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 = (ANR) malloc0 (sizeof(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 destroy_anr (ANR a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void 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 setBuffers_anr (ANR a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void setSamplerate_anr (ANR a, int rate)
|
||||
{
|
||||
flush_anr(a);
|
||||
}
|
||||
|
||||
void setSize_anr (ANR a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
flush_anr(a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT void
|
||||
SetRXAANRRun (int channel, int run)
|
||||
{
|
||||
ANR a = rxa[channel].anr.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
RXAbp1Check (channel, rxa[channel].amd.p->run, rxa[channel].snba.p->run,
|
||||
rxa[channel].emnr.p->run, rxa[channel].anf.p->run, run,
|
||||
rxa[channel].rnnr.p->run, rxa[channel].sbnr.p->run); // NR3 + NR4 support
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->run = run;
|
||||
RXAbp1Set (channel);
|
||||
flush_anr (a);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANRVals (int channel, int taps, int delay, double gain, double leakage)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anr.p->n_taps = taps;
|
||||
rxa[channel].anr.p->delay = delay;
|
||||
rxa[channel].anr.p->two_mu = gain;
|
||||
rxa[channel].anr.p->gamma = leakage;
|
||||
flush_anr (rxa[channel].anr.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANRTaps (int channel, int taps)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anr.p->n_taps = taps;
|
||||
flush_anr (rxa[channel].anr.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANRDelay (int channel, int delay)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anr.p->delay = delay;
|
||||
flush_anr (rxa[channel].anr.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANRGain (int channel, double gain)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anr.p->two_mu = gain;
|
||||
flush_anr (rxa[channel].anr.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANRLeakage (int channel, double leakage)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anr.p->gamma = leakage;
|
||||
flush_anr (rxa[channel].anr.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetRXAANRPosition (int channel, int position)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].anr.p->position = position;
|
||||
rxa[channel].bp1.p->position = position;
|
||||
flush_anr (rxa[channel].anr.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
107
anr.h
Normal file
107
anr.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* anr.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2012, 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _anr_h
|
||||
#define _anr_h
|
||||
|
||||
#define ANR_DLINE_SIZE 2048
|
||||
|
||||
typedef struct _anr
|
||||
{
|
||||
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;
|
||||
} anr, *ANR;
|
||||
|
||||
extern 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
|
||||
);
|
||||
|
||||
extern void destroy_anr (ANR a);
|
||||
|
||||
extern void flush_anr (ANR a);
|
||||
|
||||
extern void xanr (ANR a, int position);
|
||||
|
||||
extern void setBuffers_anr (ANR a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_anr (ANR a, int rate);
|
||||
|
||||
extern void setSize_anr (ANR a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRRun (int channel, int setit);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRVals (int channel, int taps, int delay, double gain, double leakage);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRTaps (int channel, int taps);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRDelay (int channel, int delay);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRGain (int channel, double gain);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRLeakage (int channel, double leakage);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAANRPosition (int channel, int position);
|
||||
|
||||
#endif
|
||||
187
apfshadow.c
Normal file
187
apfshadow.c
Normal file
@ -0,0 +1,187 @@
|
||||
/* apfshadow.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
APFSHADOW create_apfshadow (int selection, int run, double f_center, double bandwidth, double gain)
|
||||
{
|
||||
APFSHADOW a = (APFSHADOW)malloc0 (sizeof (apfshadow));
|
||||
a->selection = selection;
|
||||
a->run = run;
|
||||
a->f_center = f_center;
|
||||
a->bandwidth = bandwidth;
|
||||
a->gain = gain;
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_apfshadow (APFSHADOW a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXASPCWSelection (int channel, int selection)
|
||||
{
|
||||
APFSHADOW a = rxa[channel].apfshadow.p;
|
||||
if (a->selection != selection)
|
||||
{
|
||||
a->selection = selection;
|
||||
switch (a->selection)
|
||||
{
|
||||
case 0: // Double-pole
|
||||
SetRXAMatchedRun (channel, 0);
|
||||
SetRXAGaussianRun (channel, 0);
|
||||
SetRXABiQuadRun (channel, 0);
|
||||
SetRXADoublepoleFreqs (channel, a->f_center, a->bandwidth);
|
||||
SetRXADoublepoleGain (channel, a->gain);
|
||||
SetRXADoublepoleRun (channel, a->run);
|
||||
break;
|
||||
case 1: // Matched
|
||||
SetRXADoublepoleRun (channel, 0);
|
||||
SetRXAGaussianRun (channel, 0);
|
||||
SetRXABiQuadRun (channel, 0);
|
||||
SetRXAMatchedFreqs (channel, a->f_center, a->bandwidth);
|
||||
SetRXAMatchedGain (channel, sqrt (2.0) * a->gain);
|
||||
SetRXAMatchedRun (channel, a->run);
|
||||
break;
|
||||
case 2: // Gaussian
|
||||
SetRXADoublepoleRun (channel, 0);
|
||||
SetRXAMatchedRun (channel, 0);
|
||||
SetRXABiQuadRun (channel, 0);
|
||||
SetRXAGaussianFreqs (channel, a->f_center, a->bandwidth);
|
||||
SetRXAGaussianGain (channel, sqrt (2.0) * a->gain);
|
||||
SetRXAGaussianRun (channel, a->run);
|
||||
break;
|
||||
case 3: // Bi-quad
|
||||
SetRXADoublepoleRun (channel, 0);
|
||||
SetRXAMatchedRun (channel, 0);
|
||||
SetRXAGaussianRun (channel, 0);
|
||||
SetRXABiQuadFreq (channel, a->f_center);
|
||||
SetRXABiQuadBandwidth (channel, a->bandwidth);
|
||||
SetRXABiQuadGain (channel, a->gain);
|
||||
SetRXABiQuadRun (channel, a->run);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXASPCWRun (int channel, int run)
|
||||
{
|
||||
APFSHADOW a = rxa[channel].apfshadow.p;
|
||||
a->run = run;
|
||||
switch (a->selection)
|
||||
{
|
||||
case 0: // Double-pole
|
||||
SetRXADoublepoleRun (channel, a->run);
|
||||
break;
|
||||
case 1: // Matched
|
||||
SetRXAMatchedRun (channel, a->run);
|
||||
break;
|
||||
case 2: // Gaussian
|
||||
SetRXAGaussianRun (channel, a->run);
|
||||
break;
|
||||
case 3: // Bi-quad
|
||||
SetRXABiQuadRun (channel, a->run);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXASPCWFreq (int channel, double f_center)
|
||||
{
|
||||
APFSHADOW a = rxa[channel].apfshadow.p;
|
||||
a->f_center = f_center;
|
||||
switch (a->selection)
|
||||
{
|
||||
case 0: // Double-pole
|
||||
SetRXADoublepoleFreqs (channel, a->f_center, a->bandwidth);
|
||||
break;
|
||||
case 1: // Matched
|
||||
SetRXAMatchedFreqs (channel, a->f_center, a->bandwidth);
|
||||
break;
|
||||
case 2: // Gaussian
|
||||
SetRXAGaussianFreqs (channel, a->f_center, a->bandwidth);
|
||||
break;
|
||||
case 3: // Bi-quad
|
||||
SetRXABiQuadFreq (channel, a->f_center);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXASPCWBandwidth (int channel, double bandwidth)
|
||||
{
|
||||
APFSHADOW a = rxa[channel].apfshadow.p;
|
||||
a->bandwidth = bandwidth;
|
||||
switch (a->selection)
|
||||
{
|
||||
case 0: // Double-pole
|
||||
SetRXADoublepoleFreqs (channel, a->f_center, a->bandwidth);
|
||||
break;
|
||||
case 1: // Matched
|
||||
SetRXAMatchedFreqs (channel, a->f_center, a->bandwidth);
|
||||
break;
|
||||
case 2: // Gaussian
|
||||
SetRXAGaussianFreqs (channel, a->f_center, a->bandwidth);
|
||||
break;
|
||||
case 3: // Bi-quad
|
||||
SetRXABiQuadBandwidth (channel, a->bandwidth);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXASPCWGain (int channel, double gain)
|
||||
{
|
||||
APFSHADOW a = rxa[channel].apfshadow.p;
|
||||
a->gain = gain;
|
||||
switch (a->selection)
|
||||
{
|
||||
case 0: // Double-pole
|
||||
SetRXADoublepoleGain (channel, a->gain);
|
||||
break;
|
||||
case 1: // Matched
|
||||
SetRXAMatchedGain (channel, sqrt(2.0) * a->gain);
|
||||
break;
|
||||
case 2: // Gaussian
|
||||
SetRXAGaussianGain (channel, sqrt(2.0) * a->gain);
|
||||
break;
|
||||
case 3: // Bi-quad
|
||||
SetRXABiQuadGain (channel, a->gain);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
42
apfshadow.h
Normal file
42
apfshadow.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* apfshadow.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025 Warren Pratt, NR0V
|
||||
|
||||
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 _apfshadow_h
|
||||
#define _apfshadow_h
|
||||
typedef struct _apfshadow
|
||||
{
|
||||
int selection;
|
||||
int run;
|
||||
double f_center;
|
||||
double bandwidth;
|
||||
double gain;
|
||||
} apfshadow, *APFSHADOW;
|
||||
|
||||
extern APFSHADOW create_apfshadow (int selection, int run,
|
||||
double f_center, double bandwidth, double gain);
|
||||
|
||||
extern void destroy_apfshadow (APFSHADOW a);
|
||||
#endif
|
||||
594
bandpass.c
Normal file
594
bandpass.c
Normal file
@ -0,0 +1,594 @@
|
||||
/* bandpass.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2017 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void calc_bps (BPS a)
|
||||
{
|
||||
double* impulse;
|
||||
a->infilt = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->product = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
impulse = fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = 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);
|
||||
_aligned_free(impulse);
|
||||
}
|
||||
|
||||
void decalc_bps (BPS a)
|
||||
{
|
||||
fftw_destroy_plan(a->CRev);
|
||||
fftw_destroy_plan(a->CFor);
|
||||
_aligned_free(a->mults);
|
||||
_aligned_free(a->product);
|
||||
_aligned_free(a->infilt);
|
||||
}
|
||||
|
||||
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 = (BPS) malloc0 (sizeof (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 destroy_bps (BPS a)
|
||||
{
|
||||
decalc_bps (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_bps (BPS a)
|
||||
{
|
||||
memset (a->infilt, 0, 2 * a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
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(complex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_bps (BPS a, double* in, double* out)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
void setSamplerate_bps (BPS a, int rate)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->samplerate = rate;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
void setSize_bps (BPS a, int size)
|
||||
{
|
||||
decalc_bps (a);
|
||||
a->size = size;
|
||||
calc_bps (a);
|
||||
}
|
||||
|
||||
void 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 *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
/* // UNCOMMENT properties when a pointer is in place in rxa[channel]
|
||||
PORT
|
||||
void SetRXABPSRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].bp1.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXABPSFreqs (int channel, double f_low, double f_high)
|
||||
{
|
||||
double* impulse;
|
||||
BPS a1;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a1 = rxa[channel].bp1.p;
|
||||
if ((f_low != a1->f_low) || (f_high != a1->f_high))
|
||||
{
|
||||
a1->f_low = f_low;
|
||||
a1->f_high = f_high;
|
||||
_aligned_free (a1->mults);
|
||||
impulse = fir_bandpass(a1->size + 1, f_low, f_high, a1->samplerate, a1->wintype, 1, 1.0 / (double)(2 * a1->size));
|
||||
a1->mults = fftcv_mults (2 * a1->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXABPSWindow (int channel, int wintype)
|
||||
{
|
||||
double* impulse;
|
||||
BPS a1;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a1 = rxa[channel].bp1.p;
|
||||
if ((a1->wintype != wintype))
|
||||
{
|
||||
a1->wintype = wintype;
|
||||
_aligned_free (a1->mults);
|
||||
impulse = fir_bandpass(a1->size + 1, a1->f_low, a1->f_high, a1->samplerate, a1->wintype, 1, 1.0 / (double)(2 * a1->size));
|
||||
a1->mults = fftcv_mults (2 * a1->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
*/
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
/* // UNCOMMENT properties when pointers in place in txa[channel]
|
||||
PORT
|
||||
void SetTXABPSRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].bp1.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXABPSFreqs (int channel, double f_low, double f_high)
|
||||
{
|
||||
double* impulse;
|
||||
BPS a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].bp0.p;
|
||||
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||
{
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
_aligned_free (a->mults);
|
||||
impulse = fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp1.p;
|
||||
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||
{
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
_aligned_free (a->mults);
|
||||
impulse = fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp2.p;
|
||||
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||
{
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
_aligned_free (a->mults);
|
||||
impulse = fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXABPSWindow (int channel, int wintype)
|
||||
{
|
||||
double* impulse;
|
||||
BPS a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].bp0.p;
|
||||
if (a->wintype != wintype)
|
||||
{
|
||||
a->wintype = wintype;
|
||||
_aligned_free (a->mults);
|
||||
impulse = fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp1.p;
|
||||
if (a->wintype != wintype)
|
||||
{
|
||||
a->wintype = wintype;
|
||||
_aligned_free (a->mults);
|
||||
impulse = fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp2.p;
|
||||
if (a->wintype != wintype)
|
||||
{
|
||||
a->wintype = wintype;
|
||||
_aligned_free (a->mults);
|
||||
impulse = fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (double)(2 * a->size));
|
||||
a->mults = fftcv_mults (2 * a->size, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save 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 = (BANDPASS) malloc0 (sizeof (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_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
a->p = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_bandpass (BANDPASS a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_bandpass (BANDPASS a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xbandpass (BANDPASS a, int pos)
|
||||
{
|
||||
if (a->run && a->position == pos)
|
||||
xfircore (a->p);
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_bandpass (BANDPASS a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void setSamplerate_bandpass (BANDPASS a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
a->samplerate = rate;
|
||||
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);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setSize_bandpass (BANDPASS a, int size)
|
||||
{
|
||||
// NOTE: 'size' must be <= 'nc'
|
||||
double* impulse;
|
||||
a->size = size;
|
||||
setSize_fircore (a->p, a->size);
|
||||
// recalc impulse because scale factor is a function of size
|
||||
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);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setGain_bandpass (BANDPASS a, double gain, int update)
|
||||
{
|
||||
double* impulse;
|
||||
a->gain = gain;
|
||||
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, update);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void 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_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);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXABandpassRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].bp1.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXABandpassFreqs (int channel, double f_low, double f_high)
|
||||
{
|
||||
double* impulse;
|
||||
BANDPASS a = rxa[channel].bp1.p;
|
||||
if ((f_low != a->f_low) || (f_high != a->f_high))
|
||||
{
|
||||
impulse = fir_bandpass (a->nc, f_low, f_high, a->samplerate,
|
||||
a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
setImpulse_fircore (a->p, impulse, 0);
|
||||
_aligned_free (impulse);
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->f_low = f_low;
|
||||
a->f_high = f_high;
|
||||
setUpdate_fircore (a->p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXABandpassWindow (int channel, int wintype)
|
||||
{
|
||||
double* impulse;
|
||||
BANDPASS a = rxa[channel].bp1.p;
|
||||
if ((a->wintype != wintype))
|
||||
{
|
||||
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate,
|
||||
wintype, 1, a->gain / (double)(2 * a->size));
|
||||
setImpulse_fircore (a->p, impulse, 0);
|
||||
_aligned_free (impulse);
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->wintype = wintype;
|
||||
setUpdate_fircore (a->p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXABandpassNC (int channel, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
double* impulse;
|
||||
BANDPASS a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].bp1.p;
|
||||
if (nc != a->nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXABandpassMP (int channel, int mp)
|
||||
{
|
||||
BANDPASS a;
|
||||
a = rxa[channel].bp1.p;
|
||||
if (mp != a->mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXABandpassRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].bp1.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
//PORT
|
||||
//void SetTXABandpassFreqs (int channel, double f_low, double f_high)
|
||||
//{
|
||||
// double* impulse;
|
||||
// BANDPASS a;
|
||||
// a = txa[channel].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);
|
||||
// _aligned_free (impulse);
|
||||
// }
|
||||
// a = txa[channel].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);
|
||||
// _aligned_free (impulse);
|
||||
// }
|
||||
// a = txa[channel].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);
|
||||
// _aligned_free (impulse);
|
||||
// }
|
||||
//}
|
||||
|
||||
PORT
|
||||
void SetTXABandpassWindow (int channel, int wintype)
|
||||
{
|
||||
double* impulse;
|
||||
BANDPASS a;
|
||||
a = txa[channel].bp0.p;
|
||||
if (a->wintype != wintype)
|
||||
{
|
||||
a->wintype = wintype;
|
||||
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);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp1.p;
|
||||
if (a->wintype != wintype)
|
||||
{
|
||||
a->wintype = wintype;
|
||||
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);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp2.p;
|
||||
if (a->wintype != wintype)
|
||||
{
|
||||
a->wintype = wintype;
|
||||
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);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXABandpassNC (int channel, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
double* impulse;
|
||||
BANDPASS a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].bp0.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp1.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
a = txa[channel].bp2.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (double)(2 * a->size));
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXABandpassMP (int channel, int mp)
|
||||
{
|
||||
BANDPASS a;
|
||||
a = txa[channel].bp0.p;
|
||||
if (mp != a->mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
a = txa[channel].bp1.p;
|
||||
if (mp != a->mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
a = txa[channel].bp2.p;
|
||||
if (mp != a->mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
143
bandpass.h
Normal file
143
bandpass.h
Normal file
@ -0,0 +1,143 @@
|
||||
/* bandpass.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2017 Warren Pratt, NR0V
|
||||
|
||||
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 _bps_h
|
||||
#define _bps_h
|
||||
|
||||
typedef struct _bps
|
||||
{
|
||||
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;
|
||||
}bps, *BPS;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_bps (BPS a);
|
||||
|
||||
extern void flush_bps (BPS a);
|
||||
|
||||
extern void xbps (BPS a, int pos);
|
||||
|
||||
extern void setBuffers_bps (BPS a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_bps (BPS a, int rate);
|
||||
|
||||
extern void setSize_bps (BPS a, int size);
|
||||
|
||||
extern void setFreqs_bps (BPS a, double f_low, double f_high);
|
||||
|
||||
// RXA Prototypes
|
||||
|
||||
extern __declspec (dllexport) void SetRXABPSRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABPSFreqs (int channel, double low, double high);
|
||||
|
||||
// TXA Prototypes
|
||||
|
||||
extern __declspec (dllexport) void SetTXABPSRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetTXABPSFreqs (int channel, double low, double high);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
#ifndef _bandpass_h
|
||||
#define _bandpass_h
|
||||
#include "firmin.h"
|
||||
typedef struct _bandpass
|
||||
{
|
||||
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;
|
||||
}bandpass, *BANDPASS;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_bandpass (BANDPASS a);
|
||||
|
||||
extern void flush_bandpass (BANDPASS a);
|
||||
|
||||
extern void xbandpass (BANDPASS a, int pos);
|
||||
|
||||
extern void setBuffers_bandpass (BANDPASS a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_bandpass (BANDPASS a, int rate);
|
||||
|
||||
extern void setSize_bandpass (BANDPASS a, int size);
|
||||
|
||||
extern void setGain_bandpass (BANDPASS a, double gain, int update);
|
||||
|
||||
extern void CalcBandpassFilter (BANDPASS a, double f_low, double f_high, double gain);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABandpassFreqs (int channel, double f_low, double f_high);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABandpassNC (int channel, int nc);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABandpassMP (int channel, int mp);
|
||||
|
||||
extern __declspec (dllexport) void SetTXABandpassNC (int channel, int nc);
|
||||
|
||||
extern __declspec (dllexport) void SetTXABandpassMP (int channel, int mp);
|
||||
|
||||
#endif
|
||||
167
calcc.h
Normal file
167
calcc.h
Normal file
@ -0,0 +1,167 @@
|
||||
/* calcc.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2023 Warren Pratt, NR0V
|
||||
|
||||
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 _calcc_h
|
||||
#define _calcc_h
|
||||
#include "delay.h"
|
||||
#include "lmath.h"
|
||||
typedef struct _calcc
|
||||
{
|
||||
int channel;
|
||||
int runcal;
|
||||
int size;
|
||||
volatile long mox;
|
||||
volatile long solidmox;
|
||||
int rate;
|
||||
int ints;
|
||||
int spi;
|
||||
int nsamps;
|
||||
int npsamps;
|
||||
int pin;
|
||||
int map;
|
||||
int convex;
|
||||
int stbl;
|
||||
int scOK;
|
||||
double hw_scale;
|
||||
double rx_scale;
|
||||
double alpha;
|
||||
|
||||
int tsamps;
|
||||
double* env_TX;
|
||||
double* env_RX;
|
||||
double* x;
|
||||
double* ym;
|
||||
double* yc;
|
||||
double* ys;
|
||||
double* cat;
|
||||
|
||||
double* t;
|
||||
double* tmap;
|
||||
double* cm;
|
||||
double* cc;
|
||||
double* cs;
|
||||
double* cm_old;
|
||||
double* rxs;
|
||||
double* txs;
|
||||
double ptol;
|
||||
int* info;
|
||||
int* binfo;
|
||||
double txdel;
|
||||
BLDR ccbld;
|
||||
volatile long savecorr_bypass;
|
||||
HANDLE Sem_SaveCorr;
|
||||
volatile long restcorr_bypass;
|
||||
HANDLE Sem_RestCorr;
|
||||
volatile long calccorr_bypass;
|
||||
HANDLE Sem_CalcCorr;
|
||||
volatile long turnoff_bypass;
|
||||
HANDLE Sem_TurnOff;
|
||||
struct _ctrl
|
||||
{
|
||||
double moxdelay;
|
||||
double loopdelay;
|
||||
int state;
|
||||
int reset;
|
||||
int automode;
|
||||
int mancal;
|
||||
int turnon;
|
||||
int moxsamps;
|
||||
int moxcount;
|
||||
int count;
|
||||
int* cpi;
|
||||
int* sindex;
|
||||
int* sbase;
|
||||
int full_ints;
|
||||
int calcinprogress;
|
||||
volatile LONG calcdone;
|
||||
int waitsamps;
|
||||
int waitcount;
|
||||
double env_maxtx;
|
||||
volatile long running;
|
||||
int bs_count;
|
||||
volatile long current_state;
|
||||
CRITICAL_SECTION cs_SafeToEnd;
|
||||
} ctrl;
|
||||
struct _disp
|
||||
{
|
||||
double* x;
|
||||
double* ym;
|
||||
double* yc;
|
||||
double* ys;
|
||||
double* cm;
|
||||
double* cc;
|
||||
double* cs;
|
||||
CRITICAL_SECTION cs_disp;
|
||||
} disp;
|
||||
DELAY rxdelay;
|
||||
DELAY txdelay;
|
||||
struct _util
|
||||
{
|
||||
char savefile[256];
|
||||
char restfile[256];
|
||||
int ints;
|
||||
int channel;
|
||||
double* pm;
|
||||
double* pc;
|
||||
double* ps;
|
||||
} util;
|
||||
double* temptx; //////////////////////////////////////////////////// temporary tx complex buffer - remove with new callback3port()
|
||||
double* temprx; //////////////////////////////////////////////////// temporary rx complex buffer - remove with new callback3port()
|
||||
} calcc, *CALCC;
|
||||
|
||||
extern CALCC create_calcc (int channel, int runcal, int size, int rate, int ints, int spi, double hw_scale,
|
||||
double moxdelay, double loopdelay, double ptol, int mox, int solidmox, int pin, int map, int stbl,
|
||||
int npsamps, double alpha);
|
||||
|
||||
extern void destroy_calcc (CALCC a);
|
||||
|
||||
extern void flush_calcc (CALCC a);
|
||||
|
||||
extern __declspec(dllexport) void pscc (int channel, int size, double* tx, double* rx);
|
||||
|
||||
extern void __cdecl PSSaveCorrection(void* pargs);
|
||||
|
||||
extern void __cdecl PSRestoreCorrection(void* pargs);
|
||||
|
||||
extern void __cdecl doPSCalcCorrection(void* arg);
|
||||
|
||||
extern void __cdecl doPSTurnoff(void* arg);
|
||||
|
||||
#endif
|
||||
|
||||
// 'info' assignments:
|
||||
// 0 - builder for rx_scale
|
||||
// 1 - builder for cm
|
||||
// 2 - builder for cc
|
||||
// 3 - builder for cs
|
||||
// 4 - feedback level warning
|
||||
// 5 - count of attempted calibrations
|
||||
// 6 - results from scheck()
|
||||
// 7 - results from rxscheck()
|
||||
//
|
||||
// 13 - dogcount
|
||||
// 14 - indicates iqc_Run = 1
|
||||
// 15 - control state
|
||||
29045
calculus.c
Normal file
29045
calculus.c
Normal file
File diff suppressed because it is too large
Load Diff
8
calculus.h
Normal file
8
calculus.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _calculus_h
|
||||
#define _calculus_h
|
||||
|
||||
extern double GG[];
|
||||
|
||||
extern double GGS[];
|
||||
|
||||
#endif
|
||||
126
cblock.c
Normal file
126
cblock.c
Normal file
@ -0,0 +1,126 @@
|
||||
/* cblock.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 create_cbl
|
||||
(
|
||||
int run,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *out_buff,
|
||||
int mode,
|
||||
int sample_rate,
|
||||
double tau
|
||||
)
|
||||
{
|
||||
CBL a = (CBL) malloc0 (sizeof(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 destroy_cbl(CBL a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_cbl (CBL a)
|
||||
{
|
||||
a->prevIin = 0.0;
|
||||
a->prevQin = 0.0;
|
||||
a->prevIout = 0.0;
|
||||
a->prevQout = 0.0;
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_cbl (CBL a, double* in, double* out)
|
||||
{
|
||||
a->in_buff = in;
|
||||
a->out_buff = out;
|
||||
}
|
||||
|
||||
void setSamplerate_cbl (CBL a, int rate)
|
||||
{
|
||||
a->sample_rate = rate;
|
||||
calc_cbl (a);
|
||||
}
|
||||
|
||||
void setSize_cbl (CBL a, int size)
|
||||
{
|
||||
a->buff_size = size;
|
||||
flush_cbl (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT void
|
||||
SetRXACBLRun(int channel, int setit)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].cbl.p->run = setit;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
73
cblock.h
Normal file
73
cblock.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* cblock.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _cblock_h
|
||||
#define _cblock_h
|
||||
|
||||
typedef struct _cbl
|
||||
{
|
||||
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
|
||||
} cbl, *CBL;
|
||||
|
||||
extern CBL create_cbl
|
||||
(
|
||||
int run,
|
||||
int buff_size,
|
||||
double *in_buff,
|
||||
double *out_buff,
|
||||
int mode,
|
||||
int sample_rate,
|
||||
double tau
|
||||
);
|
||||
|
||||
extern void destroy_cbl (CBL a);
|
||||
|
||||
extern void flush_cbl (CBL a);
|
||||
|
||||
extern void xcbl (CBL a);
|
||||
|
||||
extern void setBuffers_cbl (CBL a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_cbl (CBL a, int rate);
|
||||
|
||||
extern void setSize_cbl (CBL a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXACBLRun(int channel, int setit);
|
||||
|
||||
#endif
|
||||
519
cfcomp.c
Normal file
519
cfcomp.c
Normal file
@ -0,0 +1,519 @@
|
||||
/* cfcomp.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2017, 2021 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 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 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] = max (a->F[i], 0.0);
|
||||
a->F[i] = min (a->F[i], fmax);
|
||||
a->G[i] = max (a->G[i], 0.0);
|
||||
}
|
||||
sary = (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];
|
||||
}
|
||||
_aligned_free (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);
|
||||
}
|
||||
|
||||
void 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 = (double *)malloc0 (a->fsize * sizeof(double));
|
||||
a->inaccum = (double *)malloc0 (a->iasize * sizeof(double));
|
||||
a->forfftin = (double *)malloc0 (a->fsize * sizeof(double));
|
||||
a->forfftout = (double *)malloc0 (a->msize * sizeof(complex));
|
||||
a->cmask = (double *)malloc0 (a->msize * sizeof(double));
|
||||
a->mask = (double *)malloc0 (a->msize * sizeof(double));
|
||||
a->cfc_gain = (double *)malloc0 (a->msize * sizeof(double));
|
||||
a->revfftin = (double *)malloc0 (a->msize * sizeof(complex));
|
||||
a->revfftout = (double *)malloc0 (a->fsize * sizeof(double));
|
||||
a->save = (double **)malloc0(a->ovrlp * sizeof(double *));
|
||||
for (i = 0; i < a->ovrlp; i++)
|
||||
a->save[i] = (double *)malloc0(a->fsize * sizeof(double));
|
||||
a->outaccum = (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 = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->gp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->ep = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->comp = (double *) malloc0 (a->msize * sizeof (double));
|
||||
a->peq = (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 = (double*)malloc0 (a->msize * sizeof(double));
|
||||
a->delta_copy = (double*)malloc0 (a->msize * sizeof(double));
|
||||
a->cfc_gain_copy = (double*)malloc0 (a->msize * sizeof(double));
|
||||
}
|
||||
|
||||
void decalc_cfcomp(CFCOMP a)
|
||||
{
|
||||
int i;
|
||||
_aligned_free (a->cfc_gain_copy);
|
||||
_aligned_free (a->delta_copy);
|
||||
_aligned_free (a->delta);
|
||||
_aligned_free (a->peq);
|
||||
_aligned_free (a->comp);
|
||||
_aligned_free (a->ep);
|
||||
_aligned_free (a->gp);
|
||||
_aligned_free (a->fp);
|
||||
|
||||
fftw_destroy_plan(a->Rrev);
|
||||
fftw_destroy_plan(a->Rfor);
|
||||
_aligned_free(a->outaccum);
|
||||
for (i = 0; i < a->ovrlp; i++)
|
||||
_aligned_free(a->save[i]);
|
||||
_aligned_free(a->save);
|
||||
_aligned_free(a->revfftout);
|
||||
_aligned_free(a->revfftin);
|
||||
_aligned_free(a->cfc_gain);
|
||||
_aligned_free(a->mask);
|
||||
_aligned_free(a->cmask);
|
||||
_aligned_free(a->forfftout);
|
||||
_aligned_free(a->forfftin);
|
||||
_aligned_free(a->inaccum);
|
||||
_aligned_free(a->window);
|
||||
}
|
||||
|
||||
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 = (CFCOMP) malloc0 (sizeof (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 = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->G = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->E = (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 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 destroy_cfcomp (CFCOMP a)
|
||||
{
|
||||
decalc_cfcomp (a);
|
||||
_aligned_free (a->E);
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (a->F);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
|
||||
void 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 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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_cfcomp (CFCOMP a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void setSamplerate_cfcomp (CFCOMP a, int rate)
|
||||
{
|
||||
decalc_cfcomp (a);
|
||||
a->rate = rate;
|
||||
calc_cfcomp (a);
|
||||
}
|
||||
|
||||
void setSize_cfcomp (CFCOMP a, int size)
|
||||
{
|
||||
decalc_cfcomp (a);
|
||||
a->bsize = size;
|
||||
calc_cfcomp (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXACFCOMPRun (int channel, int run)
|
||||
{
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
if (a->run != run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACFCOMPPosition (int channel, int pos)
|
||||
{
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
if (a->position != pos)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->position = pos;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACFCOMPprofile (int channel, int nfreqs, double* F, double* G, double *E)
|
||||
{
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->nfreqs = nfreqs;
|
||||
_aligned_free (a->E);
|
||||
_aligned_free (a->F);
|
||||
_aligned_free (a->G);
|
||||
a->F = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->G = (double *)malloc0 (a->nfreqs * sizeof (double));
|
||||
a->E = (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));
|
||||
_aligned_free (a->ep);
|
||||
_aligned_free (a->gp);
|
||||
_aligned_free (a->fp);
|
||||
a->fp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->gp = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
a->ep = (double *) malloc0 ((a->nfreqs + 2) * sizeof (double));
|
||||
calc_comp(a);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACFCOMPPrecomp (int channel, double precomp)
|
||||
{
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
if (a->precomp != precomp)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
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];
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACFCOMPPeqRun (int channel, int run)
|
||||
{
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
if (a->peq_run != run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->peq_run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACFCOMPPrePeq (int channel, double prepeq)
|
||||
{
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->prepeq = prepeq;
|
||||
a->prepeqlin = pow (10.0, 0.05 * a->prepeq);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void GetTXACFCOMPDisplayCompression (int channel, double* comp_values, int* ready)
|
||||
{
|
||||
int i;
|
||||
CFCOMP a = txa[channel].cfcomp.p;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
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;
|
||||
}
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
if (*ready)
|
||||
{
|
||||
for (i = 0; i < a->msize; i++)
|
||||
comp_values[i] = 20.0 * mlog10 (a->cfc_gain_copy[i] / (a->cfc_gain_copy[i] - a->delta_copy[i]));
|
||||
}
|
||||
}
|
||||
112
cfcomp.h
Normal file
112
cfcomp.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* cfcomp.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2017, 2021 Warren Pratt, NR0V
|
||||
|
||||
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 _cfcomp_h
|
||||
#define _cfcomp_h
|
||||
|
||||
typedef struct _cfcomp
|
||||
{
|
||||
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;
|
||||
}cfcomp, *CFCOMP;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_cfcomp (CFCOMP a);
|
||||
|
||||
extern void flush_cfcomp (CFCOMP a);
|
||||
|
||||
extern void xcfcomp (CFCOMP a, int pos);
|
||||
|
||||
extern void setBuffers_cfcomp (CFCOMP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_cfcomp (CFCOMP a, int rate);
|
||||
|
||||
extern void setSize_cfcomp (CFCOMP a, int size);
|
||||
|
||||
#endif
|
||||
254
cfir.c
Normal file
254
cfir.c
Normal file
@ -0,0 +1,254 @@
|
||||
/* cfir.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 2016, 2021 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#include "comm.h"
|
||||
|
||||
void 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 = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void decalc_cfir (CFIR a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
}
|
||||
|
||||
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 = (CFIR) malloc0 (sizeof (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 destroy_cfir (CFIR a)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_cfir (CFIR a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xcfir (CFIR a)
|
||||
{
|
||||
if (a->run)
|
||||
xfircore (a->p);
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_cfir (CFIR a, double* in, double* out)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
void setSamplerate_cfir (CFIR a, int rate)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->runrate = rate;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
void setSize_cfir (CFIR a, int size)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->size = size;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
void setOutRate_cfir (CFIR a, int rate)
|
||||
{
|
||||
decalc_cfir (a);
|
||||
a->cicrate = rate;
|
||||
calc_cfir (a);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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, fn, mag = 1.0;
|
||||
double* impulse;
|
||||
double* A = (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 = (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_fsamp (N, A, rtype, 1.0, wintype);
|
||||
// print_impulse ("cfirImpulse.txt", N, impulse, 1, 0);
|
||||
_aligned_free (A);
|
||||
return impulse;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXACFIRRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].cfir.p->run = run;
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACFIRNC(int channel, int nc)
|
||||
{
|
||||
// NOTE: 'nc' must be >= 'size'
|
||||
CFIR a;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
a = txa[channel].cfir.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
decalc_cfir(a);
|
||||
calc_cfir(a);
|
||||
}
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
75
cfir.h
Normal file
75
cfir.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* cfir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 2016 Warren Pratt, NR0V
|
||||
|
||||
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 _cfir_h
|
||||
#define _cfir_h
|
||||
#include "firmin.h"
|
||||
typedef struct _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;
|
||||
double scale;
|
||||
int xtype;
|
||||
double xbw;
|
||||
int wintype;
|
||||
FIRCORE p;
|
||||
} cfir, *CFIR;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_cfir (CFIR a);
|
||||
|
||||
extern void flush_cfir (CFIR a);
|
||||
|
||||
extern void xcfir (CFIR a);
|
||||
|
||||
extern void setBuffers_cfir (CFIR a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_cfir (CFIR a, int rate);
|
||||
|
||||
extern void setSize_cfir (CFIR a, int size);
|
||||
|
||||
extern void setOutRate_cfir (CFIR a, int rate);
|
||||
|
||||
extern 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);
|
||||
|
||||
extern __declspec (dllexport) void SetTXACFIRRun(int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetTXACFIRNC(int channel, int nc);
|
||||
|
||||
#endif
|
||||
348
channel.c
Normal file
348
channel.c
Normal file
@ -0,0 +1,348 @@
|
||||
/* channel.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
struct _ch ch[MAX_CHANNELS];
|
||||
|
||||
void start_thread (int channel)
|
||||
{
|
||||
HANDLE handle = (HANDLE) _beginthread(wdspmain, 0, (void *)(uintptr_t)channel);
|
||||
//SetThreadPriority(handle, THREAD_PRIORITY_HIGHEST);
|
||||
}
|
||||
|
||||
void pre_main_build (int channel)
|
||||
{
|
||||
if (ch[channel].in_rate >= ch[channel].dsp_rate)
|
||||
ch[channel].dsp_insize = ch[channel].dsp_size * (ch[channel].in_rate / ch[channel].dsp_rate);
|
||||
else
|
||||
ch[channel].dsp_insize = ch[channel].dsp_size / (ch[channel].dsp_rate / ch[channel].in_rate);
|
||||
|
||||
if (ch[channel].out_rate >= ch[channel].dsp_rate)
|
||||
ch[channel].dsp_outsize = ch[channel].dsp_size * (ch[channel].out_rate / ch[channel].dsp_rate);
|
||||
else
|
||||
ch[channel].dsp_outsize = ch[channel].dsp_size / (ch[channel].dsp_rate / ch[channel].out_rate);
|
||||
|
||||
if (ch[channel].in_rate >= ch[channel].out_rate)
|
||||
ch[channel].out_size = ch[channel].in_size / (ch[channel].in_rate / ch[channel].out_rate);
|
||||
else
|
||||
ch[channel].out_size = ch[channel].in_size * (ch[channel].out_rate / ch[channel].in_rate);
|
||||
|
||||
InitializeCriticalSectionAndSpinCount ( &ch[channel].csDSP, 2500 );
|
||||
InitializeCriticalSectionAndSpinCount ( &ch[channel].csEXCH, 2500 );
|
||||
InterlockedBitTestAndReset (&ch[channel].flushflag, 0);
|
||||
create_iobuffs (channel);
|
||||
}
|
||||
|
||||
void post_main_build (int channel)
|
||||
{
|
||||
InterlockedBitTestAndSet (&ch[channel].run, 0);
|
||||
start_thread (channel);
|
||||
if (ch[channel].state == 1)
|
||||
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
||||
}
|
||||
|
||||
void build_channel (int channel)
|
||||
{
|
||||
pre_main_build (channel);
|
||||
create_main (channel);
|
||||
post_main_build (channel);
|
||||
}
|
||||
|
||||
PORT
|
||||
void OpenChannel (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)
|
||||
{
|
||||
WDSP_FPE_GUARD;
|
||||
ch[channel].in_size = in_size;
|
||||
ch[channel].dsp_size = dsp_size;
|
||||
ch[channel].in_rate = input_samplerate;
|
||||
ch[channel].dsp_rate = dsp_rate;
|
||||
ch[channel].out_rate = output_samplerate;
|
||||
ch[channel].type = type;
|
||||
ch[channel].state = state;
|
||||
ch[channel].tdelayup = tdelayup;
|
||||
ch[channel].tslewup = tslewup;
|
||||
ch[channel].tdelaydown = tdelaydown;
|
||||
ch[channel].tslewdown = tslewdown;
|
||||
ch[channel].bfo = bfo;
|
||||
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||
build_channel (channel);
|
||||
if (ch[channel].state)
|
||||
{
|
||||
InterlockedBitTestAndSet (&ch[channel].iob.pc->slew.upflag, 0);
|
||||
InterlockedBitTestAndSet (&ch[channel].iob.ch_upslew, 0);
|
||||
InterlockedBitTestAndReset (&ch[channel].iob.pc->exec_bypass, 0);
|
||||
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
||||
}
|
||||
#if !defined(linux) && !defined(__APPLE__)
|
||||
_MM_SET_FLUSH_ZERO_MODE (_MM_FLUSH_ZERO_ON);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pre_main_destroy (int channel)
|
||||
{
|
||||
IOB a = ch[channel].iob.pc;
|
||||
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||
InterlockedBitTestAndReset (&ch[channel].run, 0);
|
||||
InterlockedBitTestAndSet (&ch[channel].iob.pc->exec_bypass, 0);
|
||||
ReleaseSemaphore (a->Sem_BuffReady, 1, 0);
|
||||
Sleep (25);
|
||||
}
|
||||
|
||||
void post_main_destroy (int channel)
|
||||
{
|
||||
destroy_iobuffs (channel);
|
||||
DeleteCriticalSection ( &ch[channel].csEXCH );
|
||||
DeleteCriticalSection ( &ch[channel].csDSP );
|
||||
}
|
||||
|
||||
PORT
|
||||
void CloseChannel (int channel)
|
||||
{
|
||||
pre_main_destroy (channel);
|
||||
destroy_main (channel);
|
||||
post_main_destroy (channel);
|
||||
}
|
||||
|
||||
void flushChannel (void* p)
|
||||
{
|
||||
int channel = (int)(uintptr_t)p;
|
||||
IOB a = ch[channel].iob.pc;
|
||||
while (!InterlockedAnd(&a->flush_bypass, 0xffffffff))
|
||||
{
|
||||
WaitForSingleObject(a->Sem_Flush, INFINITE);
|
||||
if (!InterlockedAnd(&a->flush_bypass, 0xffffffff))
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
EnterCriticalSection(&ch[channel].csEXCH);
|
||||
flush_iobuffs(channel);
|
||||
InterlockedBitTestAndSet(&a->exec_bypass, 0);
|
||||
flush_main(channel);
|
||||
LeaveCriticalSection(&ch[channel].csEXCH);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
InterlockedBitTestAndReset(&ch[channel].flushflag, 0);
|
||||
}
|
||||
}
|
||||
InterlockedBitTestAndReset(&a->flush_bypass, 0);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Channel Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetType (int channel, int type)
|
||||
{ // no need to rebuild buffers; but we did anyway
|
||||
if (type != ch[channel].type)
|
||||
{
|
||||
CloseChannel (channel);
|
||||
ch[channel].type = type;
|
||||
build_channel (channel);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetInputBuffsize (int channel, int in_size)
|
||||
{ // we do not rebuild main here since it didn't change
|
||||
if (in_size != ch[channel].in_size)
|
||||
{
|
||||
pre_main_destroy (channel);
|
||||
post_main_destroy (channel);
|
||||
ch[channel].in_size = in_size;
|
||||
pre_main_build (channel);
|
||||
post_main_build (channel);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDSPBuffsize (int channel, int dsp_size)
|
||||
{
|
||||
if (dsp_size != ch[channel].dsp_size)
|
||||
{
|
||||
int oldstate = SetChannelState (channel, 0, 1);
|
||||
pre_main_destroy (channel);
|
||||
post_main_destroy (channel);
|
||||
ch[channel].dsp_size = dsp_size;
|
||||
pre_main_build (channel);
|
||||
setDSPBuffsize_main (channel);
|
||||
post_main_build (channel);
|
||||
SetChannelState (channel, oldstate, 0);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetInputSamplerate (int channel, int in_rate)
|
||||
{ // no re-build of main required
|
||||
if (in_rate != ch[channel].in_rate)
|
||||
{
|
||||
pre_main_destroy (channel);
|
||||
post_main_destroy (channel);
|
||||
ch[channel].in_rate = in_rate;
|
||||
pre_main_build (channel);
|
||||
setInputSamplerate_main (channel);
|
||||
post_main_build (channel);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDSPSamplerate (int channel, int dsp_rate)
|
||||
{
|
||||
if (dsp_rate != ch[channel].dsp_rate)
|
||||
{
|
||||
int oldstate = SetChannelState (channel, 0, 1);
|
||||
pre_main_destroy (channel);
|
||||
post_main_destroy (channel);
|
||||
ch[channel].dsp_rate = dsp_rate;
|
||||
pre_main_build (channel);
|
||||
setDSPSamplerate_main (channel);
|
||||
post_main_build (channel);
|
||||
SetChannelState (channel, oldstate, 0);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetOutputSamplerate (int channel, int out_rate)
|
||||
{ // no re-build of main required
|
||||
if (out_rate != ch[channel].out_rate)
|
||||
{
|
||||
pre_main_destroy (channel);
|
||||
post_main_destroy (channel);
|
||||
ch[channel].out_rate = out_rate;
|
||||
pre_main_build (channel);
|
||||
setOutputSamplerate_main (channel);
|
||||
post_main_build (channel);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetAllRates (int channel, int in_rate, int dsp_rate, int out_rate)
|
||||
{
|
||||
if ((in_rate != ch[channel].in_rate) || (dsp_rate != ch[channel].dsp_rate) || (out_rate != ch[channel].out_rate))
|
||||
{
|
||||
pre_main_destroy (channel);
|
||||
post_main_destroy (channel);
|
||||
ch[channel].in_rate = in_rate;
|
||||
ch[channel].dsp_rate = dsp_rate;
|
||||
ch[channel].out_rate = out_rate;
|
||||
pre_main_build (channel);
|
||||
setInputSamplerate_main (channel);
|
||||
setDSPSamplerate_main (channel);
|
||||
setOutputSamplerate_main (channel);
|
||||
post_main_build (channel);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
int SetChannelState (int channel, int state, int dmode)
|
||||
{
|
||||
IOB a = ch[channel].iob.pc;
|
||||
int prior_state = ch[channel].state;
|
||||
int count = 0;
|
||||
const int timeout = 100;
|
||||
if (ch[channel].state != state)
|
||||
{
|
||||
ch[channel].state = state;
|
||||
switch (ch[channel].state)
|
||||
{
|
||||
case 0:
|
||||
InterlockedBitTestAndSet (&a->slew.downflag, 0);
|
||||
InterlockedBitTestAndSet (&ch[channel].flushflag, 0);
|
||||
if (dmode)
|
||||
{
|
||||
while (_InterlockedAnd (&ch[channel].flushflag, 1) && count < timeout)
|
||||
{
|
||||
Sleep(1);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count >= timeout)
|
||||
{
|
||||
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||
InterlockedBitTestAndReset (&ch[channel].flushflag, 0);
|
||||
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
InterlockedBitTestAndSet (&a->slew.upflag, 0);
|
||||
InterlockedBitTestAndSet (&ch[channel].iob.ch_upslew, 0);
|
||||
InterlockedBitTestAndReset (&ch[channel].iob.pc->exec_bypass, 0);
|
||||
InterlockedBitTestAndSet (&ch[channel].exchange, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prior_state;
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetChannelTDelayUp (int channel, double time)
|
||||
{
|
||||
IOB a;
|
||||
EnterCriticalSection (&ch[channel].csEXCH);
|
||||
a = ch[channel].iob.pc;
|
||||
ch[channel].tdelayup = time;
|
||||
a->slew.ndelup = (int)(ch[a->channel].tdelayup * ch[a->channel].in_rate);
|
||||
flush_slews (a);
|
||||
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetChannelTSlewUp (int channel, double time)
|
||||
{
|
||||
IOB a;
|
||||
EnterCriticalSection (&ch[channel].csEXCH);
|
||||
a = ch[channel].iob.pc;
|
||||
ch[channel].tslewup = time;
|
||||
destroy_slews (a);
|
||||
create_slews (a);
|
||||
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetChannelTDelayDown (int channel, double time)
|
||||
{
|
||||
IOB a;
|
||||
EnterCriticalSection (&ch[channel].csEXCH);
|
||||
a = ch[channel].iob.pc;
|
||||
ch[channel].tdelaydown = time;
|
||||
a->slew.ndeldown = (int)(ch[a->channel].tdelaydown * ch[a->channel].out_rate);
|
||||
flush_slews (a);
|
||||
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetChannelTSlewDown (int channel, double time)
|
||||
{
|
||||
IOB a;
|
||||
EnterCriticalSection (&ch[channel].csEXCH);
|
||||
a = ch[channel].iob.pc;
|
||||
ch[channel].tslewdown = time;
|
||||
destroy_slews (a);
|
||||
create_slews (a);
|
||||
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||
}
|
||||
84
channel.h
Normal file
84
channel.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* channel.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _setupchannel_h
|
||||
#define _setupchannel_h
|
||||
#include "comm.h"
|
||||
|
||||
struct _ch
|
||||
{
|
||||
int type;
|
||||
volatile long run; // when 1, thread loops; when 0, thread terminates
|
||||
volatile long exchange; // when 1, fexchange() operates; when 0, it just returns
|
||||
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
|
||||
CRITICAL_SECTION csDSP; // used to block dsp while parameters are updated or buffers flushed
|
||||
CRITICAL_SECTION 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;
|
||||
struct //io buffers
|
||||
{
|
||||
IOB pc, pd, pe, pf; // copies for console calls, dsp, exchange, and flush thread
|
||||
volatile long ch_upslew;
|
||||
} iob;
|
||||
};
|
||||
|
||||
extern struct _ch ch[];
|
||||
|
||||
PORT void OpenChannel (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);
|
||||
|
||||
PORT void CloseChannel (int channel);
|
||||
|
||||
extern void flushChannel (void* p);
|
||||
|
||||
PORT void SetType (int channel, int type);
|
||||
|
||||
PORT void SetInputBuffsize (int channel, int in_size);
|
||||
|
||||
PORT void SetDSPBuffsize (int channel, int dsp_size);
|
||||
|
||||
PORT void SetInputSamplerate (int channel, int samplerate);
|
||||
|
||||
PORT void SetDSPSamplerate (int channel, int samplerate);
|
||||
|
||||
PORT void SetOutputSamplerate (int channel, int samplerate);
|
||||
|
||||
PORT void SetAllRates (int channel, int in_rate, int dsp_rate, int out_rate);
|
||||
|
||||
PORT int SetChannelState (int channel, int state, int dmode);
|
||||
|
||||
#endif
|
||||
83
cmath.c
Normal file
83
cmath.c
Normal file
@ -0,0 +1,83 @@
|
||||
/* cmath.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
// function to calculate the magnitude of a complex value.
|
||||
double mag(double* value)
|
||||
{
|
||||
return sqrt(value[0] * value[0] + value[1] * value[1]);
|
||||
}
|
||||
|
||||
// function to perform a Complex Add, a+b; it returns a complex value, 'sum'
|
||||
void cadd(double* a, double* b, double* sum)
|
||||
{
|
||||
sum[0] = a[0] + b[0];
|
||||
sum[1] = a[1] + b[1];
|
||||
}
|
||||
|
||||
// function to perform a Complex Subtract, a-b; it returns a complex value, 'diff'
|
||||
void csub(double* a, double* b, double* diff)
|
||||
{
|
||||
diff[0] = a[0] - b[0];
|
||||
diff[1] = a[1] - b[1];
|
||||
}
|
||||
|
||||
// function to perform a Complex Multiply, a*b; it returns a complex value, 'product'
|
||||
void cmult(double* a, double* b, double* product)
|
||||
{
|
||||
product[0] = a[0] * b[0] - a[1] * b[1];
|
||||
product[1] = a[0] * b[1] + a[1] * b[0];
|
||||
}
|
||||
|
||||
// function to perform a Complex Divide, a/b; it returns a complex value, 'quotient'
|
||||
void cdiv(double* a, double* b, double* quotient)
|
||||
{
|
||||
double den = b[0] * b[0] + b[1] * b[1];
|
||||
quotient[0] = (a[0] * b[0] + a[1] * b[1]) / den;
|
||||
quotient[1] = (a[1] * b[0] - a[0] * b[1]) / den;
|
||||
}
|
||||
|
||||
// function to calculate complex Z (series equivalent) of two parallel elements
|
||||
void cpar(double* Z1, double* Z2, double* Zpar)
|
||||
{
|
||||
double num[2], den[2];
|
||||
cmult(Z1, Z2, num);
|
||||
cadd(Z1, Z2, den);
|
||||
cdiv(num, den, Zpar);
|
||||
}
|
||||
|
||||
// function to convert a complex Z to parallel R and X values
|
||||
void cser_to_par(double* Z1, double* ZR, double* ZX)
|
||||
{
|
||||
// Z1 is the sum of real and imaginary (resistive and reactive) components
|
||||
// While expressed as complex, ZR contains the resistive parallel element with imaginary component equal to zero
|
||||
// While expressed as complex, ZX contains the reactive parallel element with the real component equal to zero
|
||||
double num = Z1[0] * Z1[0] + Z1[1] * Z1[1];
|
||||
ZR[0] = num / Z1[0];
|
||||
ZR[1] = 0.0;
|
||||
ZX[0] = 0.0;
|
||||
ZX[1] = num / Z1[1];
|
||||
}
|
||||
43
cmath.h
Normal file
43
cmath.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* cmath.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025 Warren Pratt, NR0V
|
||||
|
||||
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 _cmath_h
|
||||
#define _cmath_h
|
||||
|
||||
extern double mag (double* value);
|
||||
|
||||
extern void cadd (double* a, double* b, double* sum);
|
||||
|
||||
extern void csub (double* a, double* b, double* diff);
|
||||
|
||||
extern void cmult (double* a, double* b, double* product);
|
||||
|
||||
extern void cdiv (double* a, double* b, double* quotient);
|
||||
|
||||
extern void cpar (double* Z1, double* Z2, double* Zpar);
|
||||
|
||||
extern void cser_to_par (double* Z1, double* ZR, double* ZX);
|
||||
|
||||
#endif
|
||||
150
comm.h
Normal file
150
comm.h
Normal file
@ -0,0 +1,150 @@
|
||||
/* comm.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2024, 2025 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#if defined(linux) || defined(__APPLE__)
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <string.h>
|
||||
#include "linux_port.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <process.h>
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
#include <avrt.h>
|
||||
#endif
|
||||
#include "fftw3.h"
|
||||
|
||||
#include "amd.h"
|
||||
#include "ammod.h"
|
||||
#include "amsq.h"
|
||||
#include "analyzer.h"
|
||||
#include "anf.h"
|
||||
#include "anr.h"
|
||||
#include "apfshadow.h"
|
||||
#include "bandpass.h"
|
||||
#include "calcc.h"
|
||||
#include "cblock.h"
|
||||
#include "cfcomp.h"
|
||||
#include "cfir.h"
|
||||
#include "channel.h"
|
||||
#include "cmath.h"
|
||||
#include "compress.h"
|
||||
#include "delay.h"
|
||||
#include "dexp.h"
|
||||
#include "div.h"
|
||||
#include "doublepole.h"
|
||||
#include "eer.h"
|
||||
#include "emnr.h"
|
||||
#include "rnnr.h" // NR3 + NR4 support
|
||||
#include "sbnr.h" // NR3 + NR4 support
|
||||
#include "emph.h"
|
||||
#include "eq.h"
|
||||
#include "fcurve.h"
|
||||
#include "fir.h"
|
||||
#include "firmin.h"
|
||||
#include "fmd.h"
|
||||
#include "fmmod.h"
|
||||
#include "fmsq.h"
|
||||
#include "gain.h"
|
||||
#include "gaussian.h"
|
||||
#include "gen.h"
|
||||
#include "icfir.h"
|
||||
#include "iir.h"
|
||||
#include "impulse_cache.h"
|
||||
#include "iobuffs.h"
|
||||
#include "iqc.h"
|
||||
#include "lmath.h"
|
||||
#include "main.h"
|
||||
#include "matchedCW.h"
|
||||
#include "meter.h"
|
||||
#include "meterlog10.h"
|
||||
#include "nbp.h"
|
||||
#include "nob.h"
|
||||
#include "nobII.h"
|
||||
#include "osctrl.h"
|
||||
#include "patchpanel.h"
|
||||
#include "resample.h"
|
||||
#include "rmatch.h"
|
||||
#include "RXA.h"
|
||||
#include "sender.h"
|
||||
#include "shift.h"
|
||||
#include "siphon.h"
|
||||
#include "slew.h"
|
||||
#include "snb.h"
|
||||
#include "ssql.h"
|
||||
#include "syncbuffs.h"
|
||||
#include "TXA.h"
|
||||
#include "utilities.h"
|
||||
#include "varsamp.h"
|
||||
#include "wcpAGC.h"
|
||||
|
||||
// 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 72 // 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 complex[2];
|
||||
#define PORT __declspec( dllexport )
|
||||
117
compress.c
Normal file
117
compress.c
Normal file
@ -0,0 +1,117 @@
|
||||
/* compress.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2011, 2013, 2017 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
COMPRESSOR create_compressor (
|
||||
int run,
|
||||
int buffsize,
|
||||
double* inbuff,
|
||||
double* outbuff,
|
||||
double gain )
|
||||
{
|
||||
COMPRESSOR a;
|
||||
a = (COMPRESSOR) malloc0 (sizeof (compressor));
|
||||
a->run = run;
|
||||
a->inbuff = inbuff;
|
||||
a->outbuff = outbuff;
|
||||
a->buffsize = buffsize;
|
||||
a->gain = gain;
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_compressor (COMPRESSOR a)
|
||||
{
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_compressor (COMPRESSOR a)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_compressor (COMPRESSOR a, double* in, double* out)
|
||||
{
|
||||
a->inbuff = in;
|
||||
a->outbuff = out;
|
||||
}
|
||||
|
||||
void setSamplerate_compressor (COMPRESSOR a, int rate)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void setSize_compressor (COMPRESSOR a, int size)
|
||||
{
|
||||
a->buffsize = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT void
|
||||
SetTXACompressorRun (int channel, int run)
|
||||
{
|
||||
if (txa[channel].compressor.p->run != run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].compressor.p->run = run;
|
||||
TXASetupBPFilters (channel);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
}
|
||||
|
||||
PORT void
|
||||
SetTXACompressorGain (int channel, double gain)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].compressor.p->gain = pow (10.0, gain / 20.0);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
64
compress.h
Normal file
64
compress.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* compress.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2011, 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _compressor_h
|
||||
#define _compressor_h
|
||||
|
||||
typedef struct _compressor
|
||||
{
|
||||
int run;
|
||||
int buffsize;
|
||||
double *inbuff;
|
||||
double *outbuff;
|
||||
double gain;
|
||||
} compressor, *COMPRESSOR;
|
||||
|
||||
extern void xcompressor (COMPRESSOR a);
|
||||
|
||||
extern COMPRESSOR create_compressor (
|
||||
int run,
|
||||
int buffsize,
|
||||
double* inbuff,
|
||||
double* outbuff,
|
||||
double gain );
|
||||
|
||||
extern void destroy_compressor (COMPRESSOR a);
|
||||
|
||||
extern void flush_compressor (COMPRESSOR a);
|
||||
|
||||
extern void setBuffers_compressor (COMPRESSOR a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_compressor (COMPRESSOR a, int rate);
|
||||
|
||||
extern void setSize_compressor (COMPRESSOR a, int size);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetTXACompressorRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetTXACompressorGain (int channel, double gain);
|
||||
|
||||
#endif
|
||||
135
delay.c
Normal file
135
delay.c
Normal file
@ -0,0 +1,135 @@
|
||||
/* delay.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2019 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
DELAY create_delay (int run, int size, double* in, double* out, int rate, double tdelta, double tdelay)
|
||||
{
|
||||
DELAY a = (DELAY) malloc0 (sizeof (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_bandpass (a->ncoef,-a->ft, +a->ft, 1.0, 1, 0, (double)a->L);
|
||||
a->rsize = a->cpp + (WSDEL - 1);
|
||||
a->ring = (double *) malloc0 (a->rsize * sizeof (complex));
|
||||
InitializeCriticalSectionAndSpinCount ( &a->cs_update, 2500 );
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_delay (DELAY a)
|
||||
{
|
||||
DeleteCriticalSection (&a->cs_update);
|
||||
_aligned_free (a->ring);
|
||||
_aligned_free (a->h);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_delay (DELAY a)
|
||||
{
|
||||
memset (a->ring, 0, a->cpp * sizeof (complex));
|
||||
a->idx_in = 0;
|
||||
}
|
||||
|
||||
void xdelay (DELAY a)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
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 (complex));
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void SetDelayRun (DELAY a, int run)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
double SetDelayValue (DELAY a, double tdelay)
|
||||
{
|
||||
double adelay;
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
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;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
return adelay;
|
||||
}
|
||||
|
||||
void SetDelayBuffs (DELAY a, int size, double* in, double* out)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
77
delay.h
Normal file
77
delay.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* delay.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2019 Warren Pratt, NR0V
|
||||
|
||||
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 _delay_h
|
||||
#define _delay_h
|
||||
|
||||
#define WSDEL 1025 // number of supported whole sample delays
|
||||
|
||||
typedef struct _delay
|
||||
{
|
||||
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
|
||||
|
||||
CRITICAL_SECTION cs_update;
|
||||
|
||||
} delay, *DELAY;
|
||||
|
||||
extern DELAY create_delay (int run, int size, double* in, double* out, int rate, double tdelta, double tdelay);
|
||||
|
||||
extern void destroy_delay (DELAY a);
|
||||
|
||||
extern void flush_delay (DELAY a);
|
||||
|
||||
extern void xdelay (DELAY a);
|
||||
|
||||
// Properties
|
||||
|
||||
extern void SetDelayRun (DELAY a, int run);
|
||||
|
||||
extern double SetDelayValue (DELAY a, double delay); // returns actual delay in seconds
|
||||
|
||||
extern void SetDelayBuffs (DELAY a, int size, double* in, double* out);
|
||||
|
||||
#endif
|
||||
716
dexp.c
Normal file
716
dexp.c
Normal file
@ -0,0 +1,716 @@
|
||||
/* dexp.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2018, 2019 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
DEXP pdexp[4];
|
||||
|
||||
DELRING calc_delring (int rsize, int size, int delay, double* in, double* out)
|
||||
{
|
||||
DELRING a = (DELRING) malloc0 (sizeof (delring));
|
||||
a->rsize = rsize;
|
||||
a->size = size;
|
||||
a->rdelay = delay;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->ring = (double *) malloc0 (a->rsize * sizeof (complex));
|
||||
a->inptr = a->rdelay;
|
||||
a->outptr = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
void decalc_delring (DELRING a)
|
||||
{
|
||||
_aligned_free (a->ring);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_delring (DELRING a)
|
||||
{
|
||||
memset (a->ring, 0, a->rsize * sizeof (complex));
|
||||
a->inptr = a->rdelay;
|
||||
a->outptr = 0;
|
||||
}
|
||||
|
||||
void xdelring (DELRING a)
|
||||
{
|
||||
int first, second;
|
||||
// copy in
|
||||
if (a->size > (a->rsize - a->inptr))
|
||||
{
|
||||
first = a->rsize - a->inptr;
|
||||
second = a->size - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = a->size;
|
||||
second = 0;
|
||||
}
|
||||
memcpy (a->ring + 2 * a->inptr, a->in, first * sizeof (complex));
|
||||
memcpy (a->ring, a->in + 2 * first, second * sizeof (complex));
|
||||
a->inptr = (a->inptr + a->size) % a->rsize;
|
||||
// copy out
|
||||
if (a->size > (a->rsize - a->outptr))
|
||||
{
|
||||
first = a->rsize - a->outptr;
|
||||
second = a->size - first;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = a->size;
|
||||
second = 0;
|
||||
}
|
||||
memcpy (a->out, a->ring + 2 * a->outptr, first * sizeof (complex));
|
||||
memcpy (a->out + 2 * first, a->ring, second * sizeof (complex));
|
||||
a->outptr = (a->outptr + a->size) % a->rsize;
|
||||
}
|
||||
|
||||
void calc_slews (DEXP a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
delta = PI / (double)a->nattack;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->nattack; i++)
|
||||
{
|
||||
a->cattack[i] = a->low_gain + (1.0 - a->low_gain) * 0.5 * (1.0 - cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
delta = PI / (double)a->ndecay;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ndecay; i++)
|
||||
{
|
||||
a->cdecay[i] = a->low_gain + (1.0 - a->low_gain) * 0.5 * (1.0 + cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void calc_buffs (DEXP a)
|
||||
{
|
||||
a->trigsig = (double *)malloc0 (2 * a->size * sizeof(complex)); // allow for double-sized output of filter
|
||||
a->delsig = (double *)malloc0 ( a->size * sizeof(complex));
|
||||
a->audbuffer = (double *)malloc0 ( a->size * sizeof(complex));
|
||||
}
|
||||
|
||||
void decalc_buffs (DEXP a)
|
||||
{
|
||||
_aligned_free (a->audbuffer);
|
||||
_aligned_free (a->delsig);
|
||||
_aligned_free (a->trigsig);
|
||||
}
|
||||
|
||||
void calc_dexp (DEXP a)
|
||||
{
|
||||
// trigger signal preparation
|
||||
a->avm = exp(-1.0 / (a->rate * a->dettau));
|
||||
a->onem_avm = 1.0 - a->avm;
|
||||
a->avsig = 0.0;
|
||||
// level change
|
||||
a->nattack = (int)(a->tattack * a->rate);
|
||||
a->ndecay = (int)(a->tdecay * a->rate);
|
||||
a->cattack = (double *)malloc0((a->nattack + 1) * sizeof(double));
|
||||
a->cdecay = (double *)malloc0((a->ndecay + 1) * sizeof(double));
|
||||
a->low_gain = 1.0 / a->exp_ratio;
|
||||
calc_slews(a);
|
||||
// control
|
||||
a->state = 0;
|
||||
a->count = 0;
|
||||
a->hold_thresh = a->hysteresis_ratio * a->attack_thresh; // hysteresis ratio < 1.0
|
||||
a->nhold = (int)(a->thold * a->rate);
|
||||
// vox
|
||||
a->vox_count = (int)(a->audelay * a->rate);
|
||||
// audio delay
|
||||
a->audring = calc_delring ((int)a->rate, a->size, (int)(a->audelay * a->rate), a->audbuffer, a->out);
|
||||
}
|
||||
|
||||
void decalc_dexp (DEXP a)
|
||||
{
|
||||
decalc_delring (a->audring);
|
||||
_aligned_free (a->cdecay);
|
||||
_aligned_free (a->cattack);
|
||||
}
|
||||
|
||||
void calc_filter (DEXP a)
|
||||
{
|
||||
double* impulse;
|
||||
// 2.0 gain on filter is somewhat arbitrarily chosen to get trigger input similar to that without the filter, knowing
|
||||
// that for any reasonable use of the filter there will be a reduction in trigger signal.
|
||||
impulse = fir_bandpass (a->nc, a->low_cut, a->high_cut, a->rate, a->wintype, 1, 2.0/(double)(2 * a->size));
|
||||
// print_impulse ("scf.txt", a->nc, impulse, 1, 0);
|
||||
a->p = create_fircore (a->size, a->in, a->trigsig, a->nc, 1, impulse);
|
||||
_aligned_free (impulse);
|
||||
a->scdring = calc_delring (a->size + a->nc / 2, a->size, a->nc / 64, a->in, a->delsig);
|
||||
}
|
||||
|
||||
void decalc_filter (DEXP a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
decalc_delring (a->scdring);
|
||||
}
|
||||
|
||||
void calc_antivox(DEXP a)
|
||||
{
|
||||
a->antivox_mult = exp(-1.0 / (a->antivox_rate * a->antivox_tau));
|
||||
a->antivox_onemmult = 1.0 - a->antivox_mult;
|
||||
a->antivox_data = (double *) malloc0 (a->antivox_size * sizeof (complex));
|
||||
}
|
||||
|
||||
void decalc_antivox(DEXP a)
|
||||
{
|
||||
_aligned_free (a->antivox_data);
|
||||
}
|
||||
|
||||
PORT
|
||||
void create_dexp (int id, int run_dexp, int size, double* in, double* out, int rate, double dettau, double tattack, double tdecay,
|
||||
double thold, double exp_ratio, double hyst_ratio, double attack_thresh, int nc, int wtype, double lowcut, double highcut,
|
||||
int run_filt, int run_vox, int run_audelay, double audelay, void (__stdcall *pushvox)(int id, int active),
|
||||
int antivox_run, int antivox_size, int antivox_rate, double antivox_gain, double antivox_tau)
|
||||
{
|
||||
DEXP a = (DEXP) malloc0 (sizeof (dexp));
|
||||
a->id = id;
|
||||
a->run_dexp = run_dexp;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->rate = (double)rate;
|
||||
a->dettau = dettau;
|
||||
a->tattack = tattack;
|
||||
a->tdecay = tdecay;
|
||||
a->thold = thold;
|
||||
a->exp_ratio = exp_ratio;
|
||||
a->hysteresis_ratio = hyst_ratio;
|
||||
a->attack_thresh = attack_thresh;
|
||||
a->nc = nc;
|
||||
a->wintype = wtype;
|
||||
a->low_cut = lowcut;
|
||||
a->high_cut = highcut;
|
||||
a->run_filt = run_filt;
|
||||
a->run_vox = run_vox;
|
||||
a->run_audelay = run_audelay;
|
||||
a->audelay = audelay;
|
||||
a->pushvox = pushvox;
|
||||
a->antivox_run = antivox_run;
|
||||
a->antivox_size = antivox_size;
|
||||
a->antivox_rate = (double)antivox_rate;
|
||||
a->antivox_gain = antivox_gain;
|
||||
a->antivox_tau = antivox_tau;
|
||||
calc_buffs (a);
|
||||
calc_dexp (a);
|
||||
calc_filter (a);
|
||||
calc_antivox (a);
|
||||
InitializeCriticalSectionAndSpinCount(&a->cs_update, 2500);
|
||||
pdexp[id] = a;
|
||||
return;
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_dexp (int id)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
DeleteCriticalSection (&a->cs_update);
|
||||
decalc_antivox (a);
|
||||
decalc_filter (a);
|
||||
decalc_dexp (a);
|
||||
decalc_buffs (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
PORT
|
||||
void flush_dexp (int id)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
memset (a->audbuffer, 0, a->size * sizeof (complex));
|
||||
memset (a->trigsig, 0, a->size * sizeof (complex));
|
||||
memset (a->delsig, 0, a->size * sizeof (complex));
|
||||
a->avsig = 0.0;
|
||||
a->state = 0;
|
||||
a->count = 0;
|
||||
flush_fircore (a->p);
|
||||
flush_delring (a->scdring);
|
||||
flush_delring (a->audring);
|
||||
}
|
||||
|
||||
enum _dexpstate
|
||||
{
|
||||
DEXP_LOW,
|
||||
DEXP_ATTACK,
|
||||
DEXP_HIGH,
|
||||
DEXP_HOLD,
|
||||
DEXP_DECAY
|
||||
};
|
||||
|
||||
PORT
|
||||
void xdexp (int id)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
int i;
|
||||
double sig, gain, asig;
|
||||
double max = 0.0;
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
|
||||
// ******* BEGIN SIDE-CHANNEL FILTER *******
|
||||
if (a->run_filt)
|
||||
{
|
||||
xdelring (a->scdring); // input is 'a->in'; output is 'a->delsig'
|
||||
xfircore (a->p); // input is 'a->in'; output is 'a->trigsig'
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (a->delsig, a->in, a->size * sizeof (complex));
|
||||
memcpy (a->trigsig, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
// ******* END SIDE-CHANNEL FILTER *******
|
||||
|
||||
// ******* CALCULATE ANTIVOX LEVEL *******
|
||||
if (a->state == DEXP_LOW && a->antivox_new != 0)
|
||||
{
|
||||
// if VOX is currently NOT triggered, and, if we have new antivox data to process
|
||||
for (i = 0; i < a->antivox_size; i++)
|
||||
{
|
||||
sig = sqrt (a->antivox_data[2 * i + 0] * a->antivox_data[2 * i + 0] + a->antivox_data[2 * i + 1] * a->antivox_data[2 * i + 1]);
|
||||
a->antivox_level = a->antivox_mult * a->antivox_level + a->antivox_onemmult * sig;
|
||||
}
|
||||
// set the new_data flag to zero
|
||||
a->antivox_new = 0;
|
||||
}
|
||||
// ******* END CALCULATE ANTIVOX LEVEL *******
|
||||
|
||||
// ******* BEGIN DEXP *******
|
||||
// uses 'a->trigsig' as trigger signal; uses 'a->delsig' as audio input
|
||||
// 'a->audbuffer' is audio output
|
||||
// DEXP code runs continuously so it can be used to trigger VOX also.
|
||||
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;
|
||||
if (a->avsig > max) max = a->avsig;
|
||||
switch (a->state)
|
||||
{
|
||||
case DEXP_LOW:
|
||||
if (a->antivox_run)
|
||||
asig = a->avsig - a->antivox_gain * a->antivox_level;
|
||||
else
|
||||
asig = a->avsig;
|
||||
if (asig > a->attack_thresh)
|
||||
{
|
||||
a->state = DEXP_ATTACK;
|
||||
a->count = a->nattack;
|
||||
}
|
||||
a->audbuffer[2 * i + 0] = a->low_gain * a->delsig[2 * i + 0];
|
||||
a->audbuffer[2 * i + 1] = a->low_gain * a->delsig[2 * i + 1];
|
||||
|
||||
// ******* BEGIN VOX *******
|
||||
// If we're going to attack, turn on VOX immediately.
|
||||
// Prepare 'vox_count' for the next turnoff too.
|
||||
if (a->run_vox && a->state == DEXP_ATTACK)
|
||||
{
|
||||
(a->pushvox)(a->id, 1);
|
||||
// Set vox_count for delay IF the audio delay is also enabled.
|
||||
if (a->run_audelay)
|
||||
a->vox_count = (int)(a->audelay * a->rate);
|
||||
else
|
||||
a->vox_count = 1;
|
||||
}
|
||||
// If we're sitting in this state and the delayed vox count expires, turn OFF VOX.
|
||||
else if (a->run_vox && --(a->vox_count) == 0)
|
||||
(a->pushvox)(a->id, 0);
|
||||
// Don't let 'vox_count' keep counting down.
|
||||
else if (a->vox_count < 0)
|
||||
a->vox_count = 0;
|
||||
// ******* END VOX *******
|
||||
|
||||
break;
|
||||
case DEXP_ATTACK:
|
||||
gain = a->cattack[a->nattack - a->count];
|
||||
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0] * gain;
|
||||
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1] * gain;
|
||||
if (a->count-- == 0)
|
||||
a->state = DEXP_HIGH;
|
||||
break;
|
||||
case DEXP_HIGH:
|
||||
if (a->avsig < a->hold_thresh)
|
||||
{
|
||||
a->state = DEXP_HOLD;
|
||||
a->count = a->nhold;
|
||||
}
|
||||
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0];
|
||||
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1];
|
||||
break;
|
||||
case DEXP_HOLD:
|
||||
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0];
|
||||
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1];
|
||||
if (a->avsig > a->attack_thresh)
|
||||
a->state = DEXP_HIGH;
|
||||
else if (a->count-- == 0)
|
||||
{
|
||||
a->state = DEXP_DECAY;
|
||||
a->count = a->ndecay;
|
||||
}
|
||||
break;
|
||||
case DEXP_DECAY:
|
||||
gain = a->cdecay[a->ndecay - a->count];
|
||||
a->audbuffer[2 * i + 0] = a->delsig[2 * i + 0] * gain;
|
||||
a->audbuffer[2 * i + 1] = a->delsig[2 * i + 1] * gain;
|
||||
if (a->count-- == 0)
|
||||
a->state = DEXP_LOW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
a->peak = max;
|
||||
// If DEXP functionality is set to OFF, copy its input to overwrite its output.
|
||||
if (!a->run_dexp)
|
||||
memcpy (a->audbuffer, a->delsig, a->size * sizeof (complex));
|
||||
// ******* END DEXP *******
|
||||
|
||||
// ******* BEGIN AUDIO DELAY *******
|
||||
if (a->run_audelay)
|
||||
xdelring (a->audring); // uses 'a->audbuffer' as audio input; uses 'a->out' as audio output
|
||||
else
|
||||
memcpy (a->out, a->audbuffer, a->size * sizeof (complex));
|
||||
// ******* END AUDIO DELAY *******
|
||||
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SendCBPushDexpVox (int id, void (__stdcall *pushvox)(int id, int active))
|
||||
{
|
||||
// Call to set the address of the callback to operate VOX.
|
||||
DEXP a = pdexp[id];
|
||||
a->pushvox = pushvox;
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPRun (int id, int run)
|
||||
{
|
||||
// run != 0, puts dexp in the audio processing path; otherwise, it's only used to trigger VOX
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run_dexp = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPSize (int id, int size)
|
||||
{
|
||||
// There are some constraints on the input/output buffer sizes.
|
||||
// * must be a power-of-two
|
||||
// * must be less than or equal to 'nc', the number of filter coefficients, which is also a power-of-two
|
||||
// * must be less than 'rate' samples because of the sizing of 'audring'
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
decalc_dexp (a);
|
||||
decalc_buffs (a);
|
||||
a->size = size;
|
||||
calc_buffs (a);
|
||||
calc_dexp (a);
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPIOBuffers (int id, double* in, double* out)
|
||||
{
|
||||
// Sets the input/output buffers. They can be the same.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
decalc_dexp (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_dexp (a);
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPRate (int id, double rate)
|
||||
{
|
||||
// Sets the sample rate.
|
||||
// This is used for timing and filter calculations as well as sizing 'audring'.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
decalc_dexp (a);
|
||||
a->rate = rate;
|
||||
calc_dexp (a);
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPDetectorTau (int id, double tau)
|
||||
{
|
||||
// Time-constant for smoothing the signal for detection (seconds).
|
||||
// 0.01 seconds is a good starting point to try.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->dettau = tau;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPAttackTime (int id, double time)
|
||||
{
|
||||
// Set attack time, seconds.
|
||||
// 0.002 - 0.100 should be a good range.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->tattack = time;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPReleaseTime (int id, double time)
|
||||
{
|
||||
// Set release time, seconds.
|
||||
// 0.002 - 0.999 should be a good range.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->tdecay = time;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPHoldTime (int id, double time)
|
||||
{
|
||||
// Set hold time, seconds.
|
||||
// 0.000 - 2.000 should be a good range.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->thold = time;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPExpansionRatio (int id, double ratio)
|
||||
{
|
||||
// Set expansion ratio. High_gain = 1.0; Low_gain = 1.0/exp_ratio.
|
||||
// Range of 1.0 - 30.0 should be good. Could use dB: 0.0 - 30.0dB.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->exp_ratio = ratio;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPHysteresisRatio (int id, double ratio)
|
||||
{
|
||||
// Set Hysteresis Ratio. Hold_thresh = hysteresis_ratio * Attack_thresh.
|
||||
// Expose to operator in dB: 0.0dB - 9.9dB should be good (1.000 - 0.320).
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->hysteresis_ratio = ratio;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPAttackThreshold (int id, double thresh)
|
||||
{
|
||||
// Set attack threshold.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->attack_thresh = thresh;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPFilterTaps (int id, int taps)
|
||||
{
|
||||
// Set number of taps. Must be a power of two and an even multiple of 'size'.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
a->nc = taps;
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPWindowType (int id, int type)
|
||||
{
|
||||
// Set filter window type.
|
||||
// * 0 - 4-term Blackman-Harris.
|
||||
// * 1 - 7-term Blackman-Harris.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
a->wintype = type;
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPLowCut (int id, double lowcut)
|
||||
{
|
||||
// Set side-channel filter low_cut (Hertz).
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
a->low_cut = lowcut;
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPHighCut (int id, double highcut)
|
||||
{
|
||||
// Set side-channel filter high_cut (Hertz).
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_filter (a);
|
||||
a->high_cut = highcut;
|
||||
calc_filter (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPRunSideChannelFilter (int id, int run)
|
||||
{
|
||||
// Turn OFF/ON the side-channel filter and its compensating delay.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run_filt = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPRunVox (int id, int run)
|
||||
{
|
||||
// Turn OFF/ON calls to 'pushvox(...)'.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run_vox = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPRunAudioDelay (int id, int run)
|
||||
{
|
||||
// Turn OFF/ON audio delay line.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run_audelay = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetDEXPAudioDelay (int id, double delay)
|
||||
{
|
||||
// Set the audio delay, seconds.
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_dexp (a);
|
||||
a->audelay = delay;
|
||||
calc_dexp (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void GetDEXPPeakSignal (int id, double* peak)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
*peak = a->peak;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetAntiVOXRun (int id, int run)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->antivox_run = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetAntiVOXSize (int id, int size)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_antivox(a);
|
||||
a->antivox_size = size;
|
||||
calc_antivox(a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetAntiVOXRate (int id, double rate)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_antivox(a);
|
||||
a->antivox_rate = rate;
|
||||
calc_antivox(a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetAntiVOXGain (int id, double gain)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->antivox_gain = gain;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetAntiVOXDetectorTau (int id, double tau)
|
||||
{
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
decalc_antivox (a);
|
||||
a->antivox_tau = tau;
|
||||
calc_antivox (a);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SendAntiVOXData (int id, int nsamples, double* data)
|
||||
{
|
||||
// note: 'nsamples' is not used as it has been previously specified
|
||||
DEXP a = pdexp[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
memcpy (a->antivox_data, data, a->antivox_size * sizeof (complex));
|
||||
a->antivox_new = 1;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
123
dexp.h
Normal file
123
dexp.h
Normal file
@ -0,0 +1,123 @@
|
||||
/* dexp.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2018, 2019 Warren Pratt, NR0V
|
||||
|
||||
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 _dexp_h
|
||||
#define _dexp_h
|
||||
|
||||
typedef struct _delring
|
||||
{
|
||||
int rsize; // ringsize (measured in complex samples)
|
||||
double* ring; // ring buffer
|
||||
int inptr; // ring input pointer (counts in complex samples)
|
||||
int outptr; // ring output pointer (counts in complex samples)
|
||||
int rdelay; // ring delay (measured in complex samples)
|
||||
int size; // input/output size in complex samples
|
||||
double* in; // source buffer
|
||||
double* out; // destination buffer
|
||||
} delring, *DELRING;
|
||||
|
||||
typedef struct _dexp
|
||||
{
|
||||
int id; // 'id' for this dexp
|
||||
int run_dexp; // 0 if dexp is OFF; 1 if it's ON
|
||||
int size; // size of input/output buffers
|
||||
double* in; // audio input buffer
|
||||
double* out; // audio output buffer; can be same as 'in'
|
||||
double rate; // sample rate
|
||||
double dettau; // detection averaging time constant
|
||||
double avm; // averaging multiplier
|
||||
double onem_avm; // one minus averaging multiplier
|
||||
double avsig; // averaged detection signal
|
||||
int state; // state machine control
|
||||
int count; // count variable used within a state
|
||||
double tattack; // attack time
|
||||
double tdecay; // decay time
|
||||
int nattack; // one less than total number of attack multipliers
|
||||
int ndecay; // one less than total number of decay multipliers
|
||||
double* cattack; // attack curve multipliers
|
||||
double* cdecay; // decay curve multipliers
|
||||
double attack_thresh; // attack threshold
|
||||
double hold_thresh; // hold & decay threshold
|
||||
double thold; // hold time
|
||||
int nhold; // hold count
|
||||
double exp_ratio; // expander ratio (high-gain to low-gain)
|
||||
double hysteresis_ratio; // ratio hold_thresh/attack_thresh. 0.0 < ratio < 1.0
|
||||
double low_gain; // gain when gate is closed
|
||||
double* trigsig; // buffer for trigger signal (signal after side-channel filter)
|
||||
double* delsig; // buffer for signal delayed to match trigger signal
|
||||
double peak; // peak signal value to return to console
|
||||
// side-channel bandpass filter & and buffer for compensating delay
|
||||
int run_filt; // 1 = side-channel filter and compensating delay are ON, 0 = OFF
|
||||
int nc; // number of coefficients
|
||||
int wintype; // window type
|
||||
double low_cut; // low cutoff frequency
|
||||
double high_cut; // high cutoff frequency
|
||||
FIRCORE p; // filter structure
|
||||
DELRING scdring; // delay ring for side channel
|
||||
// output audio delay to cover RF_Delay + Xmtr_delay_and_upslew
|
||||
double* audbuffer; // buffer to serve as input to audring
|
||||
int run_audelay; // 'run' variable for audio delay ring
|
||||
double audelay; // audio output delay in seconds
|
||||
DELRING audring; // audio delay ring
|
||||
// vox
|
||||
int run_vox;
|
||||
void (__stdcall *pushvox)(int channel, int active);
|
||||
int vox_count;
|
||||
// update critical section
|
||||
CRITICAL_SECTION cs_update;
|
||||
// anti-vox
|
||||
int antivox_run; // 'run' for anti-vox
|
||||
int antivox_new; // internal variable indicating new anti-vox data is available
|
||||
int antivox_size; // size of anti-vox data buffer
|
||||
double antivox_rate; // sample-rate of anti-vox data
|
||||
double antivox_tau; // time-constant of anti-vox smoothing
|
||||
double antivox_gain; // anti-vox gain factor
|
||||
double antivox_mult; // multiplier for anti-vox smoothing
|
||||
double antivox_onemmult; // one minus antivox_mult
|
||||
double antivox_level; // current anti-vox smoothed signal level
|
||||
double* antivox_data; // buffer to hold new anti-vox data
|
||||
} dexp, *DEXP;
|
||||
|
||||
extern DEXP pdexp[];
|
||||
|
||||
__declspec (dllexport) void create_dexp (int id, int run_dexp, int size, double* in, double* out, int rate, double dettau, double tattack, double tdecay,
|
||||
double thold, double exp_ratio, double hyst_ratio, double attack_thresh, int nc, int wtype, double lowcut, double highcut,
|
||||
int run_filt, int run_vox, int run_audelay, double audelay, void (__stdcall *pushvox)(int id, int active),
|
||||
int antivox_run, int antivox_size, int antivox_rate, double antivox_gain, double antivox_tau);
|
||||
|
||||
__declspec (dllexport) void destroy_dexp (int id);
|
||||
|
||||
__declspec (dllexport) void flush_dexp (int id);
|
||||
|
||||
__declspec (dllexport) void xdexp (int id);
|
||||
|
||||
__declspec (dllexport) void SetDEXPSize (int id, int size);
|
||||
|
||||
__declspec (dllexport) void SetDEXPRate (int id, double rate);
|
||||
|
||||
__declspec (dllexport) void SendAntiVOXData (int id, int nsamples, double* data);
|
||||
|
||||
#endif
|
||||
219
div.c
Normal file
219
div.c
Normal file
@ -0,0 +1,219 @@
|
||||
/* div.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
#define MAX_NR (8) // maximum number of receivers to mix
|
||||
|
||||
MDIV create_div (int run, int nr, int size, double **in, double *out)
|
||||
{
|
||||
int i;
|
||||
MDIV a = (MDIV) malloc0 (sizeof (mdiv));
|
||||
a->run = run;
|
||||
a->nr = nr;
|
||||
a->size = size;
|
||||
a->out = out;
|
||||
a->in = (double **) malloc0 ( MAX_NR * sizeof (double *));
|
||||
if (in != 0)
|
||||
for (i = 0; i < nr; i++) a->in[i] = in[i];
|
||||
a->Irotate = (double *) malloc0 (MAX_NR * sizeof (double));
|
||||
a->Qrotate = (double *) malloc0 (MAX_NR * sizeof (double));
|
||||
InitializeCriticalSectionAndSpinCount (&a->cs_update, 2500);
|
||||
for (i = 0; i < 4; i++) ///////////// legacy interface - remove
|
||||
a->legacy[i] = (double *) malloc0 (2048 * sizeof (complex)); ///////////// legacy interface - remove
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_div (MDIV a)
|
||||
{
|
||||
int i; ///////////// legacy interface - remove
|
||||
DeleteCriticalSection (&a->cs_update);
|
||||
for (i = 0; i < 4; i++) ///////////// legacy interface - remove
|
||||
_aligned_free (a->legacy[i]); ///////////// legacy interface - remove
|
||||
_aligned_free (a->Qrotate);
|
||||
_aligned_free (a->Irotate);
|
||||
_aligned_free (a->in);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_div (MDIV a)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void xdiv (MDIV a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
if (a->output != a->nr)
|
||||
{
|
||||
if (a->out != a->in[a->output])
|
||||
memcpy (a->out, a->in[a->output], a->size * sizeof (complex));
|
||||
}
|
||||
else
|
||||
{
|
||||
int i, j;
|
||||
double I, Q;
|
||||
memset (a->out, 0, a->size * sizeof (complex));
|
||||
for (i = 0; i < a->nr; i++)
|
||||
for (j = 0; j < a->size; j++)
|
||||
{
|
||||
I = a->in[i][2 * j + 0];
|
||||
Q = a->in[i][2 * j + 1];
|
||||
a->out[2 * j + 0] += a->Irotate[i] * I - a->Qrotate[i] * Q;
|
||||
a->out[2 * j + 1] += a->Irotate[i] * Q + a->Qrotate[i] * I;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
else
|
||||
memcpy (a->out, a->in[0], a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* CALLS FOR EXTERNAL USE *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#define MAX_EXT_DIVS (2) // maximum number of DIVs called from outside wdsp
|
||||
__declspec (align (16)) MDIV pdiv[MAX_EXT_DIVS]; // array of pointers for DIVs used EXTERNAL to wdsp
|
||||
|
||||
PORT
|
||||
void create_divEXT (int id, int run, int nr, int size)
|
||||
{
|
||||
pdiv[id] = create_div (run, nr, size, 0, 0);
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_divEXT (int id)
|
||||
{
|
||||
destroy_div (pdiv[id]);
|
||||
}
|
||||
|
||||
PORT
|
||||
void flush_divEXT (int id)
|
||||
{
|
||||
flush_div (pdiv[id]);
|
||||
}
|
||||
|
||||
PORT
|
||||
void xdivEXT (int id, int nsamples, double **in, double *out)
|
||||
{
|
||||
int i;
|
||||
MDIV a = pdiv[id];
|
||||
a->size = nsamples;
|
||||
a->out = out;
|
||||
for (i = 0; i < a->nr; i++) a->in[i] = in[i];
|
||||
xdiv (a);
|
||||
}
|
||||
|
||||
// 0 - does nothing; 1 - operates
|
||||
PORT
|
||||
void SetEXTDIVRun (int id, int run)
|
||||
{
|
||||
MDIV a = pdiv[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
// size of data buffer in complex samples
|
||||
PORT
|
||||
void SetEXTDIVBuffsize (int id, int size)
|
||||
{
|
||||
MDIV a = pdiv[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->size = size;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
// number of receivers being used for diversity
|
||||
PORT
|
||||
void SetEXTDIVNr (int id, int nr)
|
||||
{
|
||||
MDIV a = pdiv[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->nr = nr;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
// number of which receiver to output
|
||||
// if output==nr, mixing occurs
|
||||
PORT
|
||||
void SetEXTDIVOutput (int id, int output)
|
||||
{
|
||||
MDIV a = pdiv[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->output = output;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
// I and Q "rotate" multipliers for each receiver
|
||||
// can be set to 1.0 and 0.0 for "reference receiver"
|
||||
PORT
|
||||
void SetEXTDIVRotate (int id, int nr, double *Irotate, double *Qrotate)
|
||||
{
|
||||
MDIV a = pdiv[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
memcpy (a->Irotate, Irotate, nr * sizeof (double));
|
||||
memcpy (a->Qrotate, Qrotate, nr * sizeof (double));
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* LEGACY INTERFACE - REMOVE *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
PORT
|
||||
void xdivEXTF (int id, int size, float **input, float *Iout, float *Qout)
|
||||
{
|
||||
int i, j;
|
||||
MDIV a = pdiv[id];
|
||||
if (a->run)
|
||||
{
|
||||
a->size = size;
|
||||
for (i = 0; i < a->nr; i++)
|
||||
{
|
||||
for (j = 0; j < a->size; j++)
|
||||
{
|
||||
a->legacy[i][2 * j + 0] = (double)input[2 * i + 0][j];
|
||||
a->legacy[i][2 * j + 1] = (double)input[2 * i + 1][j];
|
||||
}
|
||||
a->in[i] = a->legacy[i];
|
||||
}
|
||||
a->out = a->legacy[3];
|
||||
xdiv (a);
|
||||
for (j = 0; j < a->size; j++)
|
||||
{
|
||||
Iout[j] = (float)a->legacy[3][2 * j + 0];
|
||||
Qout[j] = (float)a->legacy[3][2 * j + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
56
div.h
Normal file
56
div.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* div.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014 Warren Pratt, NR0V
|
||||
|
||||
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 _div_h
|
||||
#define _div_h
|
||||
|
||||
typedef struct _div
|
||||
{
|
||||
int run;
|
||||
int nr; // number of receivers to mix
|
||||
int size; // size of input/output buffers
|
||||
double **in; // input buffers
|
||||
double *out; // output buffer
|
||||
int output; // which rcvr to output; ==nr for mix
|
||||
double *Irotate;
|
||||
double *Qrotate;
|
||||
CRITICAL_SECTION cs_update;
|
||||
double *legacy[4]; ///////////// legacy interface - remove
|
||||
} mdiv, *MDIV;
|
||||
|
||||
extern MDIV create_div (int run, int nr, int size, double **in, double *out);
|
||||
|
||||
extern void destroy_div (MDIV pdiv);
|
||||
|
||||
extern void xdiv (MDIV pdiv);
|
||||
|
||||
extern __declspec(dllexport) void xdivEXT (int id, int nsamples, double **in, double *out);
|
||||
|
||||
extern __declspec(dllexport) void create_divEXT (int id, int run, int nr, int size);
|
||||
|
||||
extern __declspec(dllexport) void destroy_divEXT (int id);
|
||||
|
||||
#endif
|
||||
310
doublepole.c
Normal file
310
doublepole.c
Normal file
@ -0,0 +1,310 @@
|
||||
/* doublepole.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025-2026 Warren Pratt, NR0V
|
||||
|
||||
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 "comm.h"
|
||||
|
||||
static int calc_dpole_nc (double rate, double bandwidth)
|
||||
{
|
||||
int nc = 0;
|
||||
int rate_mult = (int)ceil((int)rate / 12000);
|
||||
int bw_mult = 1;
|
||||
bandwidth /= 1.7;
|
||||
if (bandwidth < 80.0) bw_mult = 2;
|
||||
if (bandwidth < 40.0) bw_mult = 4;
|
||||
if (bandwidth < 20.0) bw_mult = 8;
|
||||
if (bandwidth < 10.0) bw_mult = 16;
|
||||
nc = 256 * rate_mult * bw_mult;;
|
||||
if (nc < 2048) nc = 2048;
|
||||
return nc;
|
||||
}
|
||||
|
||||
static void H (double scale, double fcenter, double bandwidth, double f, double Hres[])
|
||||
{
|
||||
double a[2];
|
||||
double b[2];
|
||||
a[0] = (bandwidth / fcenter);
|
||||
a[1] = 0.0;
|
||||
b[0] = 1.0 - (f / fcenter) * (f / fcenter);
|
||||
b[1] = f * bandwidth / (fcenter * fcenter);
|
||||
cdiv (a, b, Hres);
|
||||
return;
|
||||
}
|
||||
|
||||
double* build_doublepole_1sided (int nc, double rate, double fcenter, double bandwidth, double scale)
|
||||
{
|
||||
// nc - number of impulse response values, POWER OF TWO
|
||||
// rate - sample_rate (samples/second)
|
||||
// f - center frequency (Hz)
|
||||
// bandwidth - bandwidth (Hz)
|
||||
// scale - scale factor to apply to impulse response
|
||||
double Hres[2] = { 0.0 };
|
||||
int i;
|
||||
double jd;
|
||||
double nfreqs = 3000.0;
|
||||
double* h_i = (double*)malloc0 (nc * sizeof(complex));
|
||||
for (i = 0; i < nc; i++)
|
||||
{
|
||||
double sum[2] = { 0.0 };
|
||||
double eto[2] = { 0.0 };
|
||||
double inner[2] = { 0.0 };
|
||||
for (jd = -nfreqs / 2.0; jd <= nfreqs / 2.0; jd += 1.0)
|
||||
{
|
||||
double theta = 2.0 * PI * (double)i * jd / rate;
|
||||
eto[0] = cos(theta);
|
||||
eto[1] = sin(theta);
|
||||
H(scale, fcenter, bandwidth, jd, Hres);
|
||||
cmult(Hres, eto, inner);
|
||||
sum[0] += inner[0];
|
||||
sum[1] += inner[1];
|
||||
}
|
||||
h_i[2 * i + 0] = sum[0] / (double)nc;
|
||||
h_i[2 * i + 1] = 0.0;
|
||||
}
|
||||
// print_impulse("pre_analytic.txt", nc, h_i, 1, 0);
|
||||
int npad = 8;
|
||||
int size = npad * nc;
|
||||
double* pad = (double*)malloc0 (size * sizeof(complex));
|
||||
memcpy (pad, h_i, nc * sizeof(complex));
|
||||
analytic (size, pad, pad);
|
||||
memcpy (h_i, pad, nc * sizeof(complex));
|
||||
_aligned_free (pad);
|
||||
double sum = 0.0;
|
||||
for (i = 0; i < nc; i++)
|
||||
sum += sqrt(h_i[2 * i + 0] * h_i[2 * i + 0] + h_i[2 * i + 1] * h_i[2 * i + 1]);
|
||||
for (i = 0; i < 2 * nc; i++)
|
||||
h_i[i] *= scale / sum;
|
||||
// print_impulse("dpole.txt", nc, h_i, 1, 0);
|
||||
return h_i;
|
||||
}
|
||||
|
||||
double* build_doublepole_2sided (int nc, double rate, double fcenter, double bandwidth, double scale)
|
||||
{
|
||||
// nc - number of impulse response values, POWER OF TWO
|
||||
// rate - sample_rate (samples/second)
|
||||
// f - center frequency (Hz)
|
||||
// bandwidth - bandwidth (Hz)
|
||||
// scale - scale factor to apply to impulse response
|
||||
double bw = bandwidth / 1.70;
|
||||
double Hres[2] = { 0.0 };
|
||||
int i;
|
||||
double jd;
|
||||
double delta = rate / (double)nc;
|
||||
int nfreqs = 3000;
|
||||
double mult = 2.0 * scale / (double)nc;
|
||||
double* h_i = (double*)malloc0 (nc * sizeof(complex));
|
||||
double* H_i = (double*)malloc0 (nc * sizeof(complex));
|
||||
for (i = 0, jd = 0.0; i < nfreqs / 2; i++, jd += delta)
|
||||
{
|
||||
H (scale, fcenter, bw, jd, Hres);
|
||||
H_i[2 * i + 0] = Hres[0] * mult;
|
||||
H_i[2 * i + 1] = Hres[1] * mult;
|
||||
}
|
||||
for (i = nc - nfreqs / 2, jd = -(double)nfreqs * delta; i < nc; i++, jd += delta)
|
||||
{
|
||||
H (scale, fcenter, bw, jd, Hres);
|
||||
H_i[2 * i + 0] = Hres[0] * mult;
|
||||
H_i[2 * i + 1] = Hres[1] * mult;
|
||||
}
|
||||
fftw_plan prev = fftw_plan_dft_1d (nc, (fftw_complex*)H_i,
|
||||
(fftw_complex*)h_i, FFTW_BACKWARD, FFTW_PATIENT);
|
||||
fftw_execute (prev);
|
||||
fftw_destroy_plan (prev);
|
||||
_aligned_free (H_i);
|
||||
for (i = 0; i < nc; i++)
|
||||
h_i[2 * i + 1] = 0.0;
|
||||
return h_i;
|
||||
}
|
||||
|
||||
double* build_doublepole_1eff (int nc, double rate, double fcenter, double bandwidth, double scale)
|
||||
{
|
||||
// nc - number of impulse response values, POWER OF TWO
|
||||
// rate - sample_rate (samples/second)
|
||||
// f - center frequency (Hz)
|
||||
// bandwidth - bandwidth (Hz)
|
||||
// scale - scale factor to apply to impulse response
|
||||
double bw = bandwidth / 1.7;
|
||||
double alpha = PI * bw / rate;
|
||||
double omega = - TWOPI * fcenter / rate;
|
||||
double impulse, arg;
|
||||
double* c_impulse = (double*) malloc0 (nc * sizeof(complex));
|
||||
for (int i = 0; i < nc; i++)
|
||||
{
|
||||
impulse = scale * alpha * exp (-alpha * (double)i);
|
||||
arg = omega * (double)i;
|
||||
c_impulse[2 * i + 0] = + impulse * cos (arg);
|
||||
c_impulse[2 * i + 1] = - impulse * sin (arg);
|
||||
}
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
DOUBLEPOLE create_doublepole (int run, int position, int size, double* in, double* out,
|
||||
double f_center, double bandwidth, int samplerate, double gain, int mode)
|
||||
{
|
||||
DOUBLEPOLE a = (DOUBLEPOLE)malloc0 (sizeof(doublepole));
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->f_center = f_center;
|
||||
a->bandwidth = bandwidth;
|
||||
a->samplerate = samplerate;
|
||||
a->gain = gain;
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
a->mode = mode;
|
||||
a->nc = calc_dpole_nc (a->samplerate, a->bandwidth);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
double* impulse = build_doublepole_1eff (a->nc, a->samplerate, a->f_center, a->bandwidth, a->scale);
|
||||
a->p = create_fircore (a->size, a->in, a->out, a->nc, 0, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_doublepole (DOUBLEPOLE a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_doublepole (DOUBLEPOLE a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xdoublepole (DOUBLEPOLE a, int pos)
|
||||
{
|
||||
if (a->run && a->position == pos)
|
||||
{
|
||||
// 'mode == 0' => CWL
|
||||
if (a->mode == 1) // CWU
|
||||
{
|
||||
for (int i = 0; i < a->size; i++)
|
||||
a->in[2 * i + 1] *= -1.0;
|
||||
}
|
||||
if (a->mode == 2) // CWL + CWU
|
||||
{
|
||||
for (int i = 0; i < a->size; i++)
|
||||
a->in[2 * i + 1] = a->in[2 * i + 0];
|
||||
}
|
||||
xfircore (a->p);
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof(complex));
|
||||
}
|
||||
|
||||
void setBuffers_doublepole (DOUBLEPOLE a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void setSamplerate_doublepole (DOUBLEPOLE a, int rate)
|
||||
{
|
||||
a->samplerate = rate;
|
||||
int nc = a->nc;
|
||||
a->nc = calc_dpole_nc (a->samplerate, a->bandwidth);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
double* impulse = build_doublepole_1eff (a->nc, a->samplerate, a->f_center, a->bandwidth, a->scale);
|
||||
if (nc == a->nc) setImpulse_fircore (a->p, impulse, 1);
|
||||
else setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setSize_doublepole (DOUBLEPOLE a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
setSize_fircore (a->p, a->size);
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
int nc = a->nc;
|
||||
a->nc = calc_dpole_nc (a->samplerate, a->bandwidth);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
double* impulse = build_doublepole_1eff (a->nc, a->samplerate, a->f_center, a->bandwidth, a->scale);
|
||||
if (nc == a->nc) setImpulse_fircore (a->p, impulse, 1);
|
||||
else setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setGain_doublepole (DOUBLEPOLE a, double gain)
|
||||
{
|
||||
a->gain = gain;
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
double* impulse = build_doublepole_1eff (a->nc, a->samplerate, a->f_center, a->bandwidth, a->scale);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void CalcDoublepoleFilter (DOUBLEPOLE a, double f_center, double bandwidth, double gain)
|
||||
{
|
||||
if ((a->f_center != f_center) || (a->bandwidth != bandwidth) || (a->gain != gain))
|
||||
{
|
||||
a->f_center = f_center;
|
||||
a->bandwidth = bandwidth;
|
||||
a->gain = gain;
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
int nc = a->nc;
|
||||
a->nc = calc_dpole_nc (a->samplerate, a->bandwidth);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
double* impulse = build_doublepole_1eff (a->nc, a->samplerate, a->f_center, a->bandwidth, a->scale);
|
||||
if (nc == a->nc) setImpulse_fircore (a->p, impulse, 1);
|
||||
else setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXADoublepoleRun (int channel, int run)
|
||||
{
|
||||
DOUBLEPOLE a = rxa[channel].doublepole.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXADoublepoleFreqs (int channel, double f_center, double bandwidth)
|
||||
{
|
||||
DOUBLEPOLE a = rxa[channel].doublepole.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
CalcDoublepoleFilter (a, f_center, bandwidth, a->gain);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXADoublepoleGain (int channel, double gain)
|
||||
{
|
||||
DOUBLEPOLE a = rxa[channel].doublepole.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
CalcDoublepoleFilter (a, a->f_center, a->bandwidth, gain);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
79
doublepole.h
Normal file
79
doublepole.h
Normal file
@ -0,0 +1,79 @@
|
||||
/* doublepole.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Double Pole Filter *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
#ifndef _doublepole_h
|
||||
#define _doublepole_h
|
||||
#include "firmin.h"
|
||||
|
||||
typedef struct _doublepole
|
||||
{
|
||||
int run; // 0 - filter is OFF; 1 - filter is ON
|
||||
int position; // position in sequence in which to execute the filter
|
||||
int size; // input/output buffer size
|
||||
int nc; // number of impulse response coefficients
|
||||
double* in; // pointer to input buffer
|
||||
double* out; // pointer to output buffer
|
||||
double f_center; // filter center frequency (Hz)
|
||||
double bandwidth; // filter bandwidth (Hz)
|
||||
double samplerate; // sample_rate (samples/sec)
|
||||
double gain; // gain to be applied to filter output
|
||||
double scale; // internal filter scale factor based upon gain
|
||||
int mode; // Mode to get output: 0 => CWL; 1 => CWU; 2 => CWL + CWU
|
||||
FIRCORE p;
|
||||
} doublepole, *DOUBLEPOLE;
|
||||
|
||||
extern DOUBLEPOLE create_doublepole (int run, int position, int size, double* in, double* out,
|
||||
double f_center, double bandwidth, int samplerate, double gain, int mode);
|
||||
|
||||
extern void destroy_doublepole (DOUBLEPOLE a);
|
||||
|
||||
extern void flush_doublepole (DOUBLEPOLE a);
|
||||
|
||||
extern void xdoublepole (DOUBLEPOLE a, int pos);
|
||||
|
||||
extern void setBuffers_doublepole (DOUBLEPOLE a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_doublepole (DOUBLEPOLE a, int rate);
|
||||
|
||||
extern void setSize_doublepole (DOUBLEPOLE a, int size);
|
||||
|
||||
extern void setGain_doublepole (DOUBLEPOLE a, double gain);
|
||||
|
||||
extern void CalcDoublepoleFilter (DOUBLEPOLE a, double f_center, double bandwidth, double gain);
|
||||
|
||||
extern __declspec (dllexport) void SetRXADoublepoleRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetRXADoublepoleFreqs (int channel, double f_center, double bandwidth);
|
||||
|
||||
extern __declspec (dllexport) void SetRXADoublepoleGain (int channel, double gain);
|
||||
|
||||
#endif
|
||||
393
eer.c
Normal file
393
eer.c
Normal file
@ -0,0 +1,393 @@
|
||||
/* eer.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
PORT
|
||||
EER create_eer (int run, int size, double* in, double* out, double* outM, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq)
|
||||
{
|
||||
EER a = (EER) malloc0 (sizeof (eer));
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->outM = outM;
|
||||
a->rate = rate;
|
||||
a->mgain = mgain;
|
||||
a->pgain = pgain;
|
||||
a->rundelays = rundelays;
|
||||
a->mdelay = mdelay;
|
||||
a->pdelay = pdelay;
|
||||
a->amiq = amiq;
|
||||
a->mdel = create_delay (
|
||||
a->rundelays, // run
|
||||
a->size, // size
|
||||
a->outM, // input buffer
|
||||
a->outM, // output buffer
|
||||
a->rate, // sample rate
|
||||
20.0e-09, // delta (delay stepsize)
|
||||
a->mdelay); // delay
|
||||
a->pdel = create_delay (
|
||||
a->rundelays, // run
|
||||
a->size, // size
|
||||
a->out, // input buffer
|
||||
a->out, // output buffer
|
||||
a->rate, // sample rate
|
||||
20.0e-09, // delta (delay stepsize)
|
||||
a->pdelay); // delay
|
||||
InitializeCriticalSectionAndSpinCount(&a->cs_update, 2500);
|
||||
|
||||
a->legacy = (double *) malloc0 (2048 * sizeof (complex)); /////////////// legacy interface - remove
|
||||
a->legacyM = (double *) malloc0 (2048 * sizeof (complex)); /////////////// legacy interface - remove
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_eer (EER a)
|
||||
{
|
||||
DeleteCriticalSection (&a->cs_update);
|
||||
destroy_delay (a->pdel);
|
||||
destroy_delay (a->mdel);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
PORT
|
||||
void flush_eer (EER a)
|
||||
{
|
||||
flush_delay (a->mdel);
|
||||
flush_delay (a->pdel);
|
||||
}
|
||||
|
||||
PORT
|
||||
void xeer (EER a)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double I, Q, mag;
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
I = a->in[2 * i + 0];
|
||||
Q = a->in[2 * i + 1];
|
||||
a->outM[2 * i + 0] = I * a->mgain;
|
||||
a->outM[2 * i + 1] = Q * a->mgain;
|
||||
switch (a->amiq)
|
||||
{
|
||||
case 0: // send phase info only, magnitude is constant
|
||||
mag = sqrt (I * I + Q * Q);
|
||||
a->out [2 * i + 0] = a->pgain * I / mag;
|
||||
a->out [2 * i + 1] = a->pgain * Q / mag;
|
||||
break;
|
||||
case 1: // send magnitude and phase information, I and Q
|
||||
a->out [2 * i + 0] = a->pgain * I;
|
||||
a->out [2 * i + 1] = a->pgain * Q;
|
||||
break;
|
||||
case 2: // send envelope
|
||||
mag = sqrt (I * I + Q * Q);
|
||||
a->out [2 * i + 0] = a->out[2 * i + 1] = a->pgain * mag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xdelay (a->mdel); // delay for outM
|
||||
xdelay (a->pdel); // delay for out
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* CALLS FOR EXTERNAL USE *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#define MAX_EXT_EERS (2) // maximum number of EERs called from outside wdsp
|
||||
__declspec (align (16)) EER peer[MAX_EXT_EERS]; // array of pointers for EERs used EXTERNAL to wdsp
|
||||
|
||||
|
||||
PORT
|
||||
void create_eerEXT (int id, int run, int size, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq)
|
||||
{
|
||||
peer[id] = create_eer (run, size, 0, 0, 0, rate, mgain, pgain, rundelays, mdelay, pdelay, amiq);
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_eerEXT (int id)
|
||||
{
|
||||
destroy_eer (peer[id]);
|
||||
}
|
||||
|
||||
PORT
|
||||
void flush_eerEXT (int id)
|
||||
{
|
||||
flush_eer (peer[id]);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERRun (int id, int run)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERAMIQ (int id, int amiq)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->amiq = amiq;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERMgain (int id, double gain)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->mgain = gain;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERPgain (int id, double gain)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->pgain = gain;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERRunDelays (int id, int run)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->rundelays = run;
|
||||
SetDelayRun (a->mdel, a->rundelays);
|
||||
SetDelayRun (a->pdel, a->rundelays);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERMdelay (int id, double delay)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->mdelay = delay;
|
||||
SetDelayValue (a->mdel, a->mdelay);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERPdelay (int id, double delay)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->pdelay = delay;
|
||||
SetDelayValue (a->pdel, a->pdelay);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERSize (int id, int size)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->size = size;
|
||||
SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
|
||||
SetDelayBuffs (a->pdel, a->size, a->out, a->out);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetEERSamplerate (int id, int rate)
|
||||
{
|
||||
EER a = peer[id];
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->rate = rate;
|
||||
destroy_delay (a->mdel);
|
||||
destroy_delay (a->pdel);
|
||||
a->mdel = create_delay (
|
||||
a->rundelays, // run
|
||||
a->size, // size
|
||||
a->outM, // input buffer
|
||||
a->outM, // output buffer
|
||||
a->rate, // sample rate
|
||||
20.0e-09, // delta (delay stepsize)
|
||||
a->mdelay); // delay
|
||||
a->pdel = create_delay (
|
||||
a->rundelays, // run
|
||||
a->size, // size
|
||||
a->out, // input buffer
|
||||
a->out, // output buffer
|
||||
a->rate, // sample rate
|
||||
20.0e-09, // delta (delay stepsize)
|
||||
a->pdelay); // delay
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* POINTER-BASED PROPERTIES *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void pSetEERRun (EER a, int run)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERAMIQ (EER a, int amiq)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->amiq = amiq;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERMgain (EER a, double gain)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->mgain = gain;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERPgain (EER a, double gain)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->pgain = gain;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERRunDelays (EER a, int run)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->rundelays = run;
|
||||
SetDelayRun (a->mdel, a->rundelays);
|
||||
SetDelayRun (a->pdel, a->rundelays);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERMdelay (EER a, double delay)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->mdelay = delay;
|
||||
SetDelayValue (a->mdel, a->mdelay);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERPdelay (EER a, double delay)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->pdelay = delay;
|
||||
SetDelayValue (a->pdel, a->pdelay);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERSize (EER a, int size)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->size = size;
|
||||
SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
|
||||
SetDelayBuffs (a->pdel, a->size, a->out, a->out);
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetEERSamplerate (EER a, int rate)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->rate = rate;
|
||||
destroy_delay (a->mdel);
|
||||
destroy_delay (a->pdel);
|
||||
a->mdel = create_delay (
|
||||
a->rundelays, // run
|
||||
a->size, // size
|
||||
a->outM, // input buffer
|
||||
a->outM, // output buffer
|
||||
a->rate, // sample rate
|
||||
20.0e-09, // delta (delay stepsize)
|
||||
a->mdelay); // delay
|
||||
a->pdel = create_delay (
|
||||
a->rundelays, // run
|
||||
a->size, // size
|
||||
a->out, // input buffer
|
||||
a->out, // output buffer
|
||||
a->rate, // sample rate
|
||||
20.0e-09, // delta (delay stepsize)
|
||||
a->pdelay); // delay
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Legacy Interface *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void xeerEXTF (int id, float* inI, float* inQ, float* outI, float* outQ, float* outMI, float* outMQ, int mox, int size)
|
||||
{
|
||||
EER a = peer[id];
|
||||
if (mox && a->run)
|
||||
{
|
||||
int i;
|
||||
a->in = a->legacy;
|
||||
a->out = a->legacy;
|
||||
a->outM = a->legacyM;
|
||||
a->size = size;
|
||||
SetDelayBuffs (a->mdel, a->size, a->outM, a->outM);
|
||||
SetDelayBuffs (a->pdel, a->size, a->out, a->out);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
a->legacy[2 * i + 0] = (double)inI[i];
|
||||
a->legacy[2 * i + 1] = (double)inQ[i];
|
||||
}
|
||||
xeer (a);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
outI[i] = (float)a->legacy [2 * i + 0];
|
||||
outQ[i] = (float)a->legacy [2 * i + 1];
|
||||
outMI[i] = (float)a->legacyM[2 * i + 0];
|
||||
outMQ[i] = (float)a->legacyM[2 * i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
77
eer.h
Normal file
77
eer.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* eer.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014 Warren Pratt, NR0V
|
||||
|
||||
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 _eer_h
|
||||
#define _eer_h
|
||||
|
||||
typedef struct _eer
|
||||
{
|
||||
int run;
|
||||
int amiq;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double* outM;
|
||||
int rate;
|
||||
double mgain;
|
||||
double pgain;
|
||||
int rundelays;
|
||||
double mdelay;
|
||||
double pdelay;
|
||||
DELAY mdel;
|
||||
DELAY pdel;
|
||||
CRITICAL_SECTION cs_update;
|
||||
double *legacy; //////////// legacy interface - remove
|
||||
double *legacyM; //////////// legacy interface - remove
|
||||
} eer, *EER;
|
||||
|
||||
__declspec (dllexport) EER create_eer (int run, int size, double* in, double* out, double* outM, int rate, double mgain, double pgain, int rundelays, double mdelay, double pdelay, int amiq);
|
||||
|
||||
__declspec (dllexport) void destroy_eer (EER a);
|
||||
|
||||
__declspec (dllexport) void flush_eer (EER a);
|
||||
|
||||
__declspec (dllexport) void xeer (EER a);
|
||||
|
||||
__declspec (dllexport) void pSetEERRun (EER a, int run);
|
||||
|
||||
__declspec (dllexport) void pSetEERAMIQ (EER a, int amiq);
|
||||
|
||||
__declspec (dllexport) void pSetEERMgain (EER a, double gain);
|
||||
|
||||
__declspec (dllexport) void pSetEERPgain (EER a, double gain);
|
||||
|
||||
__declspec (dllexport) void pSetEERRunDelays (EER a, int run);
|
||||
|
||||
__declspec (dllexport) void pSetEERMdelay (EER a, double delay);
|
||||
|
||||
__declspec (dllexport) void pSetEERPdelay (EER a, double delay);
|
||||
|
||||
__declspec (dllexport) void pSetEERSize (EER a, int size);
|
||||
|
||||
__declspec (dllexport) void pSetEERSamplerate (EER a, int rate);
|
||||
|
||||
#endif
|
||||
221
emnr.h
Normal file
221
emnr.h
Normal file
@ -0,0 +1,221 @@
|
||||
/* emnr.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015 Warren Pratt, NR0V
|
||||
|
||||
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 _emnr_h
|
||||
#define _emnr_h
|
||||
|
||||
typedef struct _emnr
|
||||
{
|
||||
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 xi_min;
|
||||
double q;
|
||||
double gmax;
|
||||
//
|
||||
double* GG;
|
||||
double* GGS;
|
||||
FILE* fileb;
|
||||
//
|
||||
int dim_zeta;
|
||||
double* zeta_hat;
|
||||
int* zeta_true;
|
||||
double z_gamma_min;
|
||||
double z_gamma_max;
|
||||
double z_xihat_min;
|
||||
double z_xihat_max;
|
||||
double zeta_thresh;
|
||||
} 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 _npestl
|
||||
{
|
||||
double rate;
|
||||
int msize;
|
||||
int incr;
|
||||
double* Ysq;
|
||||
double* P;
|
||||
double* Pmin;
|
||||
double* p;
|
||||
double* D;
|
||||
double* lambda_d;
|
||||
|
||||
double eta;
|
||||
double gamma;
|
||||
double beta;
|
||||
double delta_LF;
|
||||
double delta_MF;
|
||||
double delta_0;
|
||||
double delta_1;
|
||||
double delta_2;
|
||||
double alpha_d;
|
||||
double alpha_p;
|
||||
} npl;
|
||||
struct _ae
|
||||
{
|
||||
int msize;
|
||||
double* lambda_y;
|
||||
double zetaThresh;
|
||||
double psi;
|
||||
double* nmask;
|
||||
double t2;
|
||||
} ae;
|
||||
struct _post2
|
||||
{
|
||||
int run;
|
||||
double nlevel;
|
||||
double factor;
|
||||
double taper;
|
||||
double tc_decay;
|
||||
double rate_decay;
|
||||
double* w;
|
||||
int noise_frames;
|
||||
int noise_frame_index;
|
||||
double* noise_frame;
|
||||
double olddmag;
|
||||
} post2;
|
||||
}emnr, *EMNR;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_emnr (EMNR a);
|
||||
|
||||
extern void flush_emnr (EMNR a);
|
||||
|
||||
extern void xemnr (EMNR a, int pos);
|
||||
|
||||
extern void setBuffers_emnr (EMNR a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_emnr (EMNR a, int rate);
|
||||
|
||||
extern void setSize_emnr (EMNR a, int size);
|
||||
|
||||
#endif
|
||||
270
emph.c
Normal file
270
emph.c
Normal file
@ -0,0 +1,270 @@
|
||||
/* emph.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 2016, 2023 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save FM Pre-Emphasis *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
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 = (EMPHP) malloc0 (sizeof (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 = 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 = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_emphp (EMPHP a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_emphp (EMPHP a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xemphp (EMPHP a, int position)
|
||||
{
|
||||
if (a->run && a->position == position)
|
||||
xfircore (a->p);
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_emphp (EMPHP a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void setSamplerate_emphp (EMPHP a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
a->rate = rate;
|
||||
impulse = 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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setSize_emphp (EMPHP a, int size)
|
||||
{
|
||||
double* impulse;
|
||||
a->size = size;
|
||||
setSize_fircore (a->p, a->size);
|
||||
impulse = 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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save FM Pre-Emphasis: TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXAFMEmphPosition (int channel, int position)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].preemph.p->position = position;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMEmphMP (int channel, int mp)
|
||||
{
|
||||
EMPHP a;
|
||||
a = txa[channel].preemph.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMEmphNC (int channel, int nc)
|
||||
{
|
||||
EMPHP a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].preemph.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = 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);
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMPreEmphFreqs (int channel, double low, double high)
|
||||
{
|
||||
EMPHP a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].preemph.p;
|
||||
if (a->f_low != low || a->f_high != high)
|
||||
{
|
||||
a->f_low = low;
|
||||
a->f_high = high;
|
||||
impulse = 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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save FM Pre-Emphasis *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void calc_emph (EMPH a)
|
||||
{
|
||||
a->infilt = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->product = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->mults = 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 decalc_emph (EMPH a)
|
||||
{
|
||||
fftw_destroy_plan(a->CRev);
|
||||
fftw_destroy_plan(a->CFor);
|
||||
_aligned_free(a->mults);
|
||||
_aligned_free(a->product);
|
||||
_aligned_free(a->infilt);
|
||||
}
|
||||
|
||||
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 = (EMPH) malloc0 (sizeof (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 destroy_emph (EMPH a)
|
||||
{
|
||||
decalc_emph (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_emph (EMPH a)
|
||||
{
|
||||
memset (a->infilt, 0, 2 * a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
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(complex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_emph (EMPH a, double* in, double* out)
|
||||
{
|
||||
decalc_emph (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_emph (a);
|
||||
}
|
||||
|
||||
void setSamplerate_emph (EMPH a, int rate)
|
||||
{
|
||||
decalc_emph (a);
|
||||
a->rate = rate;
|
||||
calc_emph (a);
|
||||
}
|
||||
|
||||
void 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)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].preemph.p->position = position;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
*/
|
||||
116
emph.h
Normal file
116
emph.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* emph.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 2016, 2023 Warren Pratt, NR0V
|
||||
|
||||
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 _emphp_h
|
||||
#define _emphp_h
|
||||
#include "firmin.h"
|
||||
typedef struct _emphp
|
||||
{
|
||||
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;
|
||||
} emphp, *EMPHP;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_emphp (EMPHP a);
|
||||
|
||||
extern void flush_emphp (EMPHP a);
|
||||
|
||||
extern void xemphp (EMPHP a, int position);
|
||||
|
||||
extern void setBuffers_emphp (EMPHP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_emphp (EMPHP a, int rate);
|
||||
|
||||
extern void setSize_emphp (EMPHP a, int size);
|
||||
|
||||
__declspec (dllexport) void SetTXAFMEmphMP (int channel, int mp);
|
||||
|
||||
__declspec (dllexport) void SetTXAFMEmphNC (int channel, int nc);
|
||||
|
||||
__declspec (dllexport) void SetTXAFMPreEmphFreqs(int channel, double low, double high);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save FM Pre-Emphasis *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _emph_h
|
||||
#define _emph_h
|
||||
|
||||
typedef struct _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;
|
||||
} emph, *EMPH;
|
||||
|
||||
extern EMPH create_emph (int run, int position, int size, double* in, double* out, int rate, int ctype, double f_low, double f_high);
|
||||
|
||||
extern void destroy_emph (EMPH a);
|
||||
|
||||
extern void flush_emph (EMPH a);
|
||||
|
||||
extern void xemph (EMPH a, int position);
|
||||
|
||||
extern void setBuffers_emph (EMPH a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_emph (EMPH a, int rate);
|
||||
|
||||
extern void setSize_emph (EMPH a, int size);
|
||||
|
||||
#endif
|
||||
889
eq.c
Normal file
889
eq.c
Normal file
@ -0,0 +1,889 @@
|
||||
/* eq.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2017, 2025 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
int 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* eq_impulse (int N, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype)
|
||||
{
|
||||
// check for previous in the cache
|
||||
struct Params
|
||||
{
|
||||
int N;
|
||||
int nfreqs;
|
||||
int ctfmode;
|
||||
int wintype;
|
||||
double samplerate;
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct Params params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.N = N;
|
||||
params.nfreqs = nfreqs;
|
||||
params.ctfmode = ctfmode;
|
||||
params.wintype = wintype;
|
||||
params.samplerate = samplerate;
|
||||
params.scale = scale;
|
||||
|
||||
HASH_T h = fnv1a_hash(¶ms, sizeof(params));
|
||||
|
||||
size_t arr_len = (nfreqs + 1) * sizeof(double);
|
||||
HASH_T hf = fnv1a_hash((uint8_t*)F, arr_len);
|
||||
h ^= hf + GOLDEN_RATIO + (h << 6) + (h >> 2);
|
||||
HASH_T hg = fnv1a_hash((uint8_t*)G, arr_len);
|
||||
h ^= hg + GOLDEN_RATIO + (h << 6) + (h >> 2);
|
||||
|
||||
double* imp = get_impulse_cache_entry(EQ_CACHE, h, N);
|
||||
if (imp) return imp;
|
||||
//
|
||||
|
||||
double* fp = (double *) malloc0 ((nfreqs + 2) * sizeof (double));
|
||||
double* gp = (double *) malloc0 ((nfreqs + 2) * sizeof (double));
|
||||
double* A = (double *) malloc0 ((N / 2 + 1) * sizeof (double));
|
||||
double* sary = (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_fsamp_odd(N, A, 1, 1.0, wintype);
|
||||
else
|
||||
impulse = fir_fsamp(N, A, 1, 1.0, wintype);
|
||||
// print_impulse("eq.txt", N, impulse, 1, 0);
|
||||
_aligned_free (sary);
|
||||
_aligned_free (A);
|
||||
_aligned_free (gp);
|
||||
_aligned_free (fp);
|
||||
|
||||
// store in cache
|
||||
add_impulse_to_cache(EQ_CACHE, h, N, impulse);
|
||||
|
||||
return impulse;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Equalizer *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
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 = (EQP) malloc0 (sizeof (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 = (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));
|
||||
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 = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_eqp (EQP a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_eqp (EQP a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xeqp (EQP a)
|
||||
{
|
||||
if (a->run)
|
||||
xfircore (a->p);
|
||||
else
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_eqp (EQP a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void 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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setSize_eqp (EQP a, int size)
|
||||
{
|
||||
double* impulse;
|
||||
a->size = size;
|
||||
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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Equalizer: RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXAEQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].eqp.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQNC (int channel, int nc)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].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);
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQMP (int channel, int mp)
|
||||
{
|
||||
EQP a;
|
||||
a = rxa[channel].eqp.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQProfile (int channel, int nfreqs, double* F, double* G)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = rxa[channel].eqp.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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));
|
||||
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G,
|
||||
a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQCtfmode (int channel, int mode)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = rxa[channel].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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQWintype (int channel, int wintype)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = rxa[channel].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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGrphEQ (int channel, int *rxeq)
|
||||
{ // three band equalizer (legacy compatibility)
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = rxa[channel].eqp.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGrphEQ10 (int channel, int *rxeq)
|
||||
{ // ten band equalizer (legacy compatibility)
|
||||
EQP a;
|
||||
double* impulse;
|
||||
int i;
|
||||
a = rxa[channel].eqp.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Equalizer: TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXAEQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].eqp.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQNC (int channel, int nc)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].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);
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQMP (int channel, int mp)
|
||||
{
|
||||
EQP a;
|
||||
a = txa[channel].eqp.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQProfile (int channel, int nfreqs, double* F, double* G)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = txa[channel].eqp.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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));
|
||||
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQCtfmode (int channel, int mode)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = txa[channel].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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQWintype (int channel, int wintype)
|
||||
{
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = txa[channel].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);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAGrphEQ (int channel, int *txeq)
|
||||
{ // three band equalizer (legacy compatibility)
|
||||
EQP a;
|
||||
double* impulse;
|
||||
a = txa[channel].eqp.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAGrphEQ10 (int channel, int *txeq)
|
||||
{ // ten band equalizer (legacy compatibility)
|
||||
EQP a;
|
||||
double* impulse;
|
||||
int i;
|
||||
a = txa[channel].eqp.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Equalizer *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
double* 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 = fftcv_mults(2 * size, impulse);
|
||||
_aligned_free (impulse);
|
||||
return mults;
|
||||
}
|
||||
|
||||
void calc_eq (EQ a)
|
||||
{
|
||||
a->scale = 1.0 / (double)(2 * a->size);
|
||||
a->infilt = (double *)malloc0(2 * a->size * sizeof(complex));
|
||||
a->product = (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 = eq_mults(a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
}
|
||||
|
||||
void decalc_eq (EQ a)
|
||||
{
|
||||
fftw_destroy_plan(a->CRev);
|
||||
fftw_destroy_plan(a->CFor);
|
||||
_aligned_free(a->mults);
|
||||
_aligned_free(a->product);
|
||||
_aligned_free(a->infilt);
|
||||
}
|
||||
|
||||
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 = (EQ) malloc0 (sizeof (eq));
|
||||
a->run = run;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
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));
|
||||
a->ctfmode = ctfmode;
|
||||
a->wintype = wintype;
|
||||
a->samplerate = (double)samplerate;
|
||||
calc_eq (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_eq (EQ a)
|
||||
{
|
||||
decalc_eq (a);
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (a->F);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_eq (EQ a)
|
||||
{
|
||||
memset (a->infilt, 0, 2 * a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void xeq (EQ a)
|
||||
{
|
||||
int i;
|
||||
double I, Q;
|
||||
if (a->run)
|
||||
{
|
||||
memcpy (&(a->infilt[2 * a->size]), a->in, a->size * sizeof (complex));
|
||||
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(complex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_eq (EQ a, double* in, double* out)
|
||||
{
|
||||
decalc_eq (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_eq (a);
|
||||
}
|
||||
|
||||
void setSamplerate_eq (EQ a, int rate)
|
||||
{
|
||||
decalc_eq (a);
|
||||
a->samplerate = rate;
|
||||
calc_eq (a);
|
||||
}
|
||||
|
||||
void 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[channel]
|
||||
PORT
|
||||
void SetRXAEQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].eq.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQProfile (int channel, int nfreqs, double* F, double* G)
|
||||
{
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].eq.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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));
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQCtfmode (int channel, int mode)
|
||||
{
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].eq.p;
|
||||
a->ctfmode = mode;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAEQWintype (int channel, int wintype)
|
||||
{
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].eq.p;
|
||||
a->wintype = wintype;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGrphEQ (int channel, int *rxeq)
|
||||
{ // three band equalizer (legacy compatibility)
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].eq.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGrphEQ10 (int channel, int *rxeq)
|
||||
{ // ten band equalizer (legacy compatibility)
|
||||
EQ a;
|
||||
int i;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].eq.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
*/
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Equalizer: TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
/* // UNCOMMENT properties when a pointer is in place in rxa[channel]
|
||||
PORT
|
||||
void SetTXAEQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].eq.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQProfile (int channel, int nfreqs, double* F, double* G)
|
||||
{
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].eq.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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));
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQCtfmode (int channel, int mode)
|
||||
{
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].eq.p;
|
||||
a->ctfmode = mode;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAEQMethod (int channel, int wintype)
|
||||
{
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].eq.p;
|
||||
a->wintype = wintype;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAGrphEQ (int channel, int *txeq)
|
||||
{ // three band equalizer (legacy compatibility)
|
||||
EQ a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].eq.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAGrphEQ10 (int channel, int *txeq)
|
||||
{ // ten band equalizer (legacy compatibility)
|
||||
EQ a;
|
||||
int i;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].eq.p;
|
||||
_aligned_free (a->G);
|
||||
_aligned_free (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;
|
||||
_aligned_free (a->mults);
|
||||
a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
*/
|
||||
127
eq.h
Normal file
127
eq.h
Normal file
@ -0,0 +1,127 @@
|
||||
/* eq.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||
|
||||
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 _eqp_h
|
||||
#define _eqp_h
|
||||
#include "firmin.h"
|
||||
typedef struct _eqp
|
||||
{
|
||||
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;
|
||||
} eqp, *EQP;
|
||||
|
||||
extern double* eq_impulse (int N, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype);
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_eqp (EQP a);
|
||||
|
||||
extern void flush_eqp (EQP a);
|
||||
|
||||
extern void xeqp (EQP a);
|
||||
|
||||
extern void setBuffers_eqp (EQP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_eqp (EQP a, int rate);
|
||||
|
||||
extern void setSize_eqp (EQP a, int size);
|
||||
|
||||
__declspec (dllexport) void SetRXAEQNC (int channel, int nc);
|
||||
|
||||
__declspec (dllexport) void SetRXAEQMP (int channel, int mp);
|
||||
|
||||
__declspec (dllexport) void SetTXAEQNC (int channel, int nc);
|
||||
|
||||
__declspec (dllexport) void SetTXAEQMP (int channel, int mp);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Overlap-Save Equalizer *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _eq_h
|
||||
#define _eq_h
|
||||
|
||||
typedef struct _eq
|
||||
{
|
||||
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;
|
||||
}eq, *EQ;
|
||||
|
||||
extern double* eq_mults (int size, int nfreqs, double* F, double* G, double samplerate, double scale, int ctfmode, int wintype);
|
||||
|
||||
extern EQ create_eq (int run, int size, double *in, double *out, int nfreqs, double* F, double* G, int ctfmode, int wintype, int samplerate);
|
||||
|
||||
extern void destroy_eq (EQ a);
|
||||
|
||||
extern void flush_eq (EQ a);
|
||||
|
||||
extern void xeq (EQ a);
|
||||
|
||||
extern void setBuffers_eq (EQ a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_eq (EQ a, int rate);
|
||||
|
||||
extern void setSize_eq (EQ a, int size);
|
||||
|
||||
#endif
|
||||
0
fastmath.h
Normal file
0
fastmath.h
Normal file
191
fcurve.c
Normal file
191
fcurve.c
Normal file
@ -0,0 +1,191 @@
|
||||
/* fcurve.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2025 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
double* fc_impulse (int nc, double f0, double f1, double g0, double g1, int curve, double samplerate, double scale, int ctfmode, int wintype)
|
||||
{
|
||||
// check for previous in the cache
|
||||
struct Params
|
||||
{
|
||||
int nc;
|
||||
int curve;
|
||||
int ctfmode;
|
||||
int wintype;
|
||||
double f0;
|
||||
double f1;
|
||||
double g0;
|
||||
double g1;
|
||||
double samplerate;
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct Params params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.nc = nc;
|
||||
params.curve = curve;
|
||||
params.ctfmode = ctfmode;
|
||||
params.wintype = wintype;
|
||||
params.f0 = f0;
|
||||
params.f1 = f1;
|
||||
params.g0 = g0;
|
||||
params.g1 = g1;
|
||||
params.samplerate = samplerate;
|
||||
params.scale = scale;
|
||||
|
||||
HASH_T h = fnv1a_hash(¶ms, sizeof(params));
|
||||
double* imp = get_impulse_cache_entry(FC_CACHE, h, nc);
|
||||
if (imp) return imp;
|
||||
//
|
||||
|
||||
double* A = (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_fsamp_odd(nc, A, 1, 1.0, wintype);
|
||||
else
|
||||
impulse = fir_fsamp(nc, A, 1, 1.0, wintype);
|
||||
// print_impulse ("emph.txt", size + 1, impulse, 1, 0);
|
||||
_aligned_free (A);
|
||||
|
||||
// store in cache
|
||||
add_impulse_to_cache(FC_CACHE, h, nc, impulse);
|
||||
|
||||
return impulse;
|
||||
}
|
||||
|
||||
// generate mask for Overlap-Save Filter
|
||||
double* 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 = fftcv_mults(2 * size, impulse);
|
||||
_aligned_free (impulse);
|
||||
return mults;
|
||||
}
|
||||
34
fcurve.h
Normal file
34
fcurve.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* fcurve.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||
|
||||
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 _fcurve_h
|
||||
#define _fcurve_h
|
||||
|
||||
extern double* fc_impulse (int nc, double f0, double f1, double g0, double g1, int curve, double samplerate, double scale, int ctfmode, int wintype);
|
||||
|
||||
extern double* fc_mults (int size, double f0, double f1, double g0, double g1, int curve, double samplerate, double scale, int ctfmode, int wintype);
|
||||
|
||||
#endif
|
||||
471
fir.c
Normal file
471
fir.c
Normal file
@ -0,0 +1,471 @@
|
||||
/* fir.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2022, 2025 Warren Pratt, NR0V
|
||||
|
||||
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 "comm.h"
|
||||
|
||||
double* fftcv_mults (int NM, double* c_impulse)
|
||||
{
|
||||
double* mults = (double *) malloc0 (NM * sizeof (complex));
|
||||
double* cfft_impulse = (double *) malloc0 (NM * sizeof (complex));
|
||||
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 (complex));
|
||||
// store complex coefs right-justified in the buffer
|
||||
memcpy (&(cfft_impulse[NM - 2]), c_impulse, (NM / 2 + 1) * sizeof(complex));
|
||||
fftw_execute (ptmp);
|
||||
fftw_destroy_plan (ptmp);
|
||||
_aligned_free (cfft_impulse);
|
||||
return mults;
|
||||
}
|
||||
|
||||
double* get_fsamp_window(int N, int wintype)
|
||||
{
|
||||
int i;
|
||||
double arg0, arg1;
|
||||
double* window = (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_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 = (double *) malloc0 (N * sizeof (complex));
|
||||
double *c_impulse = (double *) malloc0 (N * sizeof (complex));
|
||||
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);
|
||||
_aligned_free (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;
|
||||
}
|
||||
_aligned_free (window);
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
double* fir_fsamp (int N, double* A, int rtype, double scale, int wintype)
|
||||
{
|
||||
int n, i, j, k;
|
||||
double sum;
|
||||
double* window;
|
||||
double *c_impulse = (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;
|
||||
}
|
||||
_aligned_free (window);
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
double* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale)
|
||||
{
|
||||
// check for previous in the cache
|
||||
struct Params
|
||||
{
|
||||
int N;
|
||||
int wintype;
|
||||
int rtype;
|
||||
double f_low;
|
||||
double f_high;
|
||||
double samplerate;
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct Params params;
|
||||
memset(¶ms, 0, sizeof (params));
|
||||
params.N = N;
|
||||
params.wintype = wintype;
|
||||
params.rtype = rtype;
|
||||
params.f_low = f_low;
|
||||
params.f_high = f_high;
|
||||
params.samplerate = samplerate;
|
||||
params.scale = scale;
|
||||
|
||||
HASH_T h = fnv1a_hash(¶ms, sizeof(params));
|
||||
double* imp = get_impulse_cache_entry(FIR_CACHE, h, N);
|
||||
if (imp) return imp;
|
||||
//
|
||||
|
||||
double *c_impulse = (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
|
||||
default:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// store in cache
|
||||
add_impulse_to_cache(FIR_CACHE, h, N, c_impulse);
|
||||
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
double *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 = (double *) malloc0 (N * sizeof (complex));
|
||||
if (file = fopen(filename, "r"))
|
||||
{
|
||||
int error = 0;
|
||||
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:
|
||||
if (error == 0 && fscanf(file, "%le", &I) != 1) error = 1;
|
||||
if (error == 0)
|
||||
c_impulse[i] = +scale * I;
|
||||
break;
|
||||
case 1:
|
||||
if (error == 0 && (fscanf(file, "%le", &I) != 1 || fscanf(file, "%le", &Q) != 1)) error = 1;
|
||||
if (error == 0)
|
||||
{
|
||||
c_impulse[2 * i + 0] = +scale * I;
|
||||
c_impulse[2 * i + 1] = -scale * Q;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
void 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 = (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);
|
||||
_aligned_free (x);
|
||||
}
|
||||
|
||||
void mp_imp (int N, double* fir, double* mpfir, int pfactor, int polarity)
|
||||
{
|
||||
// check for previous in the cache
|
||||
struct Params
|
||||
{
|
||||
int N;
|
||||
int pfactor;
|
||||
int polarity;
|
||||
};
|
||||
|
||||
struct Params params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.N = N;
|
||||
params.pfactor = pfactor;
|
||||
params.polarity = polarity;
|
||||
|
||||
HASH_T h = fnv1a_hash(¶ms, sizeof(params));
|
||||
|
||||
size_t arr_len = N * sizeof(complex);
|
||||
HASH_T hf = fnv1a_hash((uint8_t*)fir, arr_len);
|
||||
h ^= hf + GOLDEN_RATIO + (h << 6) + (h >> 2);
|
||||
|
||||
double* imp = get_impulse_cache_entry(MP_CACHE, h, N);
|
||||
if (imp)
|
||||
{
|
||||
memcpy(mpfir, imp, N * sizeof(complex)); // need to copy into mpfir
|
||||
_aligned_free (imp);
|
||||
return;
|
||||
}
|
||||
//
|
||||
|
||||
int i;
|
||||
int size = N * pfactor;
|
||||
double inv_PN = 1.0 / (double)size;
|
||||
double* firpad = (double *) malloc0 (size * sizeof (complex));
|
||||
double* firfreq = (double *) malloc0 (size * sizeof (complex));
|
||||
double* mag = (double *) malloc0 (size * sizeof (double));
|
||||
double* ana = (double *) malloc0 (size * sizeof (complex));
|
||||
double* impulse = (double *) malloc0 (size * sizeof (complex));
|
||||
double* newfreq = (double *) malloc0 (size * sizeof (complex));
|
||||
memcpy (firpad, fir, N * sizeof (complex));
|
||||
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 (complex));
|
||||
else
|
||||
memcpy (mpfir, impulse, N * sizeof (complex));
|
||||
// print_impulse("min_imp.txt", N, mpfir, 1, 0);
|
||||
fftw_destroy_plan (prev);
|
||||
fftw_destroy_plan (pfor);
|
||||
_aligned_free (newfreq);
|
||||
_aligned_free (impulse);
|
||||
_aligned_free (ana);
|
||||
_aligned_free (mag);
|
||||
_aligned_free (firfreq);
|
||||
_aligned_free (firpad);
|
||||
|
||||
// store in cache
|
||||
add_impulse_to_cache(MP_CACHE, h, N, mpfir);
|
||||
}
|
||||
|
||||
// impulse response of a zero frequency filter comprising a cascade of two resonators,
|
||||
// each followed by a detrending filter
|
||||
double* 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 = (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 = (double*)malloc0 (n_dresdet * sizeof(double));
|
||||
double div = (double)((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor
|
||||
double* c_dresdet = (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);
|
||||
_aligned_free(dresdet);
|
||||
_aligned_free(resdet);
|
||||
return c_dresdet;
|
||||
}
|
||||
43
fir.h
Normal file
43
fir.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* fir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2022, 2025 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
extern double* fftcv_mults (int NM, double* c_impulse);
|
||||
|
||||
extern double* fir_fsamp_odd (int N, double* A, int rtype, double scale, int wintype);
|
||||
|
||||
extern double* fir_fsamp (int N, double* A, int rtype, double scale, int wintype);
|
||||
|
||||
extern double* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale);
|
||||
|
||||
extern double* get_fsamp_window(int N, int wintype);
|
||||
|
||||
extern double *fir_read (int N, const char *filename, int rtype, double scale);
|
||||
|
||||
extern void analytic (int N, double* in, double* out);
|
||||
|
||||
extern void mp_imp (int N, double* fir, double* mpfir, int pfactor, int polarity);
|
||||
|
||||
extern double* zff_impulse(int nc, double scale);
|
||||
491
firmin.c
Normal file
491
firmin.c
Normal file
@ -0,0 +1,491 @@
|
||||
/* firmin.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2016, 2025 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Time-Domain FIR *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void calc_firmin (FIRMIN a)
|
||||
{
|
||||
a->h = 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 = (double *) malloc0 (a->rsize * sizeof (complex));
|
||||
a->idx = 0;
|
||||
}
|
||||
|
||||
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 = (FIRMIN) malloc0 (sizeof (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 destroy_firmin (FIRMIN a)
|
||||
{
|
||||
_aligned_free (a->ring);
|
||||
_aligned_free (a->h);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_firmin (FIRMIN a)
|
||||
{
|
||||
memset (a->ring, 0, a->rsize * sizeof (complex));
|
||||
a->idx = 0;
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_firmin (FIRMIN a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void setSamplerate_firmin (FIRMIN a, int rate)
|
||||
{
|
||||
a->samplerate = (double)rate;
|
||||
calc_firmin (a);
|
||||
}
|
||||
|
||||
void setSize_firmin (FIRMIN a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
void 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 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 = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fftout = (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->fmask = (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->maskgen = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->pcfor = (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
a->maskplan = (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
a->fftout[i] = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fmask[i] = (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 = (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 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_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(complex));
|
||||
fftw_execute (a->maskplan[i]);
|
||||
}
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
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 = (FIROPT) malloc0 (sizeof (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 deplan_firopt (FIROPT a)
|
||||
{
|
||||
int i;
|
||||
fftw_destroy_plan (a->crev);
|
||||
_aligned_free (a->accum);
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
_aligned_free (a->fftout[i]);
|
||||
_aligned_free (a->fmask[i]);
|
||||
fftw_destroy_plan (a->pcfor[i]);
|
||||
fftw_destroy_plan (a->maskplan[i]);
|
||||
}
|
||||
_aligned_free (a->maskplan);
|
||||
_aligned_free (a->pcfor);
|
||||
_aligned_free (a->maskgen);
|
||||
_aligned_free (a->fmask);
|
||||
_aligned_free (a->fftout);
|
||||
_aligned_free (a->fftin);
|
||||
}
|
||||
|
||||
void destroy_firopt (FIROPT a)
|
||||
{
|
||||
deplan_firopt (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_firopt (FIROPT a)
|
||||
{
|
||||
int i;
|
||||
memset (a->fftin, 0, 2 * a->size * sizeof (complex));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
memset (a->fftout[i], 0, 2 * a->size * sizeof (complex));
|
||||
a->buffidx = 0;
|
||||
}
|
||||
|
||||
void 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 (complex));
|
||||
fftw_execute (a->pcfor[a->buffidx]);
|
||||
k = a->buffidx;
|
||||
memset (a->accum, 0, 2 * a->size * sizeof (complex));
|
||||
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(complex));
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_firopt (FIROPT a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
deplan_firopt (a);
|
||||
plan_firopt (a);
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
void setSamplerate_firopt (FIROPT a, int rate)
|
||||
{
|
||||
a->samplerate = rate;
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
void setSize_firopt (FIROPT a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
deplan_firopt (a);
|
||||
plan_firopt (a);
|
||||
calc_firopt (a);
|
||||
}
|
||||
|
||||
void 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 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 = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fftout = (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->fmask = (double ***) malloc0 (2 * sizeof (double **));
|
||||
a->fmask[0] = (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->fmask[1] = (double **) malloc0 (a->nfor * sizeof (double *));
|
||||
a->maskgen = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->pcfor = (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
a->maskplan = (fftw_plan **) malloc0 (2 * sizeof (fftw_plan *));
|
||||
a->maskplan[0] = (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
a->maskplan[1] = (fftw_plan *) malloc0 (a->nfor * sizeof (fftw_plan));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
a->fftout[i] = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fmask[0][i] = (double *) malloc0 (2 * a->size * sizeof (complex));
|
||||
a->fmask[1][i] = (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 = (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 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)
|
||||
mp_imp (a->nc, a->impulse, a->imp, 16, 0);
|
||||
else
|
||||
memcpy (a->imp, a->impulse, a->nc * sizeof (complex));
|
||||
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(complex));
|
||||
fftw_execute (a->maskplan[1 - a->cset][i]);
|
||||
}
|
||||
a->masks_ready = 1;
|
||||
if (flip)
|
||||
{
|
||||
EnterCriticalSection (&a->update);
|
||||
a->cset = 1 - a->cset;
|
||||
LeaveCriticalSection (&a->update);
|
||||
a->masks_ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FIRCORE create_fircore (int size, double* in, double* out, int nc, int mp, double* impulse)
|
||||
{
|
||||
FIRCORE a = (FIRCORE) malloc0 (sizeof (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 = (double *) malloc0 (a->nc * sizeof (complex));
|
||||
a->imp = (double *) malloc0 (a->nc * sizeof (complex));
|
||||
memcpy (a->impulse, impulse, a->nc * sizeof (complex));
|
||||
calc_fircore (a, 1);
|
||||
return a;
|
||||
}
|
||||
|
||||
void deplan_fircore (FIRCORE a)
|
||||
{
|
||||
int i;
|
||||
fftw_destroy_plan (a->crev);
|
||||
_aligned_free (a->accum);
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
{
|
||||
_aligned_free (a->fftout[i]);
|
||||
_aligned_free (a->fmask[0][i]);
|
||||
_aligned_free (a->fmask[1][i]);
|
||||
fftw_destroy_plan (a->pcfor[i]);
|
||||
fftw_destroy_plan (a->maskplan[0][i]);
|
||||
fftw_destroy_plan (a->maskplan[1][i]);
|
||||
}
|
||||
_aligned_free (a->maskplan[0]);
|
||||
_aligned_free (a->maskplan[1]);
|
||||
_aligned_free (a->maskplan);
|
||||
_aligned_free (a->pcfor);
|
||||
_aligned_free (a->maskgen);
|
||||
_aligned_free (a->fmask[0]);
|
||||
_aligned_free (a->fmask[1]);
|
||||
_aligned_free (a->fmask);
|
||||
_aligned_free (a->fftout);
|
||||
_aligned_free (a->fftin);
|
||||
}
|
||||
|
||||
void destroy_fircore (FIRCORE a)
|
||||
{
|
||||
deplan_fircore (a);
|
||||
_aligned_free (a->imp);
|
||||
_aligned_free (a->impulse);
|
||||
DeleteCriticalSection (&a->update);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_fircore (FIRCORE a)
|
||||
{
|
||||
int i;
|
||||
memset (a->fftin, 0, 2 * a->size * sizeof (complex));
|
||||
for (i = 0; i < a->nfor; i++)
|
||||
memset (a->fftout[i], 0, 2 * a->size * sizeof (complex));
|
||||
a->buffidx = 0;
|
||||
}
|
||||
|
||||
void xfircore (FIRCORE a)
|
||||
{
|
||||
int i, j, k;
|
||||
memcpy (&(a->fftin[2 * a->size]), a->in, a->size * sizeof (complex));
|
||||
fftw_execute (a->pcfor[a->buffidx]);
|
||||
k = a->buffidx;
|
||||
memset (a->accum, 0, 2 * a->size * sizeof (complex));
|
||||
EnterCriticalSection (&a->update);
|
||||
double* accum = a->accum;
|
||||
double** fftout = a->fftout;
|
||||
double*** fmask = a->fmask;
|
||||
int cset = a->cset;
|
||||
int idxmask = a->idxmask;
|
||||
int sz = a->size;
|
||||
int nfor = a->nfor;
|
||||
for (j = 0; j < nfor; j++)
|
||||
{
|
||||
for (i = 0; i < 2 * sz; i++)
|
||||
{
|
||||
accum[2 * i + 0] += fftout[k][2 * i + 0] * fmask[cset][j][2 * i + 0] - fftout[k][2 * i + 1] * fmask[cset][j][2 * i + 1];
|
||||
accum[2 * i + 1] += fftout[k][2 * i + 0] * fmask[cset][j][2 * i + 1] + fftout[k][2 * i + 1] * fmask[cset][j][2 * i + 0];
|
||||
}
|
||||
k = (k + idxmask) & idxmask;
|
||||
}
|
||||
LeaveCriticalSection (&a->update);
|
||||
a->buffidx = (a->buffidx + 1) & idxmask;
|
||||
fftw_execute (a->crev);
|
||||
memcpy (a->fftin, &(a->fftin[2 * a->size]), a->size * sizeof(complex));
|
||||
}
|
||||
|
||||
void 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 setSize_fircore (FIRCORE a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
deplan_fircore (a);
|
||||
plan_fircore (a);
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void setImpulse_fircore (FIRCORE a, double* impulse, int update)
|
||||
{
|
||||
memcpy (a->impulse, impulse, a->nc * sizeof (complex));
|
||||
calc_fircore (a, update);
|
||||
}
|
||||
|
||||
void 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);
|
||||
_aligned_free (a->impulse);
|
||||
_aligned_free (a->imp);
|
||||
a->nc = nc;
|
||||
plan_fircore (a);
|
||||
a->imp = (double *) malloc0 (a->nc * sizeof (complex));
|
||||
a->impulse = (double *) malloc0 (a->nc * sizeof (complex));
|
||||
memcpy (a->impulse, impulse, a->nc * sizeof (complex));
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void setMp_fircore (FIRCORE a, int mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
calc_fircore (a, 1);
|
||||
}
|
||||
|
||||
void setUpdate_fircore (FIRCORE a)
|
||||
{
|
||||
if (a->masks_ready)
|
||||
{
|
||||
EnterCriticalSection (&a->update);
|
||||
a->cset = 1 - a->cset;
|
||||
LeaveCriticalSection (&a->update);
|
||||
a->masks_ready = 0;
|
||||
}
|
||||
}
|
||||
184
firmin.h
Normal file
184
firmin.h
Normal file
@ -0,0 +1,184 @@
|
||||
/* firmin.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2016 Warren Pratt, NR0V
|
||||
|
||||
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 _firmin_h
|
||||
#define _firmin_h
|
||||
|
||||
typedef struct _firmin
|
||||
{
|
||||
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
|
||||
}firmin, *FIRMIN;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_firmin (FIRMIN a);
|
||||
|
||||
extern void flush_firmin (FIRMIN a);
|
||||
|
||||
extern void xfirmin (FIRMIN a, int pos);
|
||||
|
||||
extern void setBuffers_firmin (FIRMIN a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_firmin (FIRMIN a, int rate);
|
||||
|
||||
extern void setSize_firmin (FIRMIN a, int size);
|
||||
|
||||
extern void setFreqs_firmin (FIRMIN a, double f_low, double f_high);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Standalone Partitioned Overlap-Save Bandpass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _firopt_h
|
||||
#define _firopt_h
|
||||
|
||||
typedef struct _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
|
||||
} firopt, *FIROPT;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void xfiropt (FIROPT a, int pos);
|
||||
|
||||
extern void destroy_firopt (FIROPT a);
|
||||
|
||||
extern void flush_firopt (FIROPT a);
|
||||
|
||||
extern void setBuffers_firopt (FIROPT a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_firopt (FIROPT a, int rate);
|
||||
|
||||
extern void setSize_firopt (FIROPT a, int size);
|
||||
|
||||
extern void setFreqs_firopt (FIROPT a, double f_low, double f_high);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Filter Kernel *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _fircore_h
|
||||
#define _fircore_h
|
||||
|
||||
typedef struct _fircore
|
||||
{
|
||||
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
|
||||
CRITICAL_SECTION update;
|
||||
int cset;
|
||||
int mp;
|
||||
int masks_ready;
|
||||
} fircore, *FIRCORE;
|
||||
|
||||
extern FIRCORE create_fircore (int size, double* in, double* out,
|
||||
int nc, int mp, double* impulse);
|
||||
|
||||
extern void xfircore (FIRCORE a);
|
||||
|
||||
extern void destroy_fircore (FIRCORE a);
|
||||
|
||||
extern void flush_fircore (FIRCORE a);
|
||||
|
||||
extern void setBuffers_fircore (FIRCORE a, double* in, double* out);
|
||||
|
||||
extern void setSize_fircore (FIRCORE a, int size);
|
||||
|
||||
extern void setImpulse_fircore (FIRCORE a, double* impulse, int update);
|
||||
|
||||
extern void setNc_fircore (FIRCORE a, int nc, double* impulse);
|
||||
|
||||
extern void setMp_fircore (FIRCORE a, int mp);
|
||||
|
||||
extern void setUpdate_fircore (FIRCORE a);
|
||||
|
||||
#endif
|
||||
384
fmd.c
Normal file
384
fmd.c
Normal file
@ -0,0 +1,384 @@
|
||||
/* fmd.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2023 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 = create_snotch(1, a->size, a->out, a->out, (int)a->rate, a->ctcss_freq, 0.0002);
|
||||
// detector limiter
|
||||
a->plim = 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 decalc_fmd (FMD a)
|
||||
{
|
||||
destroy_wcpagc(a->plim);
|
||||
destroy_snotch(a->sntch);
|
||||
}
|
||||
|
||||
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 = (FMD) malloc0 (sizeof (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 = (double *) malloc0 (a->size * sizeof (complex));
|
||||
impulse = 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 = create_fircore (a->size, a->audio, a->out, a->nc_de, a->mp_de, impulse);
|
||||
_aligned_free (impulse);
|
||||
// audio filter
|
||||
impulse = 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 = create_fircore (a->size, a->out, a->out, a->nc_aud, a->mp_aud, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_fmd (FMD a)
|
||||
{
|
||||
destroy_fircore (a->paud);
|
||||
destroy_fircore (a->pde);
|
||||
_aligned_free (a->audio);
|
||||
decalc_fmd (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_fmd (FMD a)
|
||||
{
|
||||
memset (a->audio, 0, a->size * sizeof (complex));
|
||||
flush_fircore (a->pde);
|
||||
flush_fircore (a->paud);
|
||||
a->phs = 0.0;
|
||||
a->fil_out = 0.0;
|
||||
a->omega = 0.0;
|
||||
a->fmdc = 0.0;
|
||||
flush_snotch (a->sntch);
|
||||
flush_wcpagc (a->plim);
|
||||
}
|
||||
|
||||
void 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
|
||||
xfircore (a->pde);
|
||||
// audio filter
|
||||
xfircore (a->paud);
|
||||
// CTCSS Removal
|
||||
xsnotch (a->sntch);
|
||||
if (a->lim_run)
|
||||
{
|
||||
for (i = 0; i < 2 * a->size; i++)
|
||||
a->out[i] *= a->lim_pre_gain;
|
||||
xwcpagc (a->plim);
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_fmd (FMD a, double* in, double* out)
|
||||
{
|
||||
decalc_fmd (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_fmd (a);
|
||||
setBuffers_fircore (a->pde, a->audio, a->out);
|
||||
setBuffers_fircore (a->paud, a->out, a->out);
|
||||
setBuffers_wcpagc (a->plim, a->out, a->out);
|
||||
}
|
||||
|
||||
void setSamplerate_fmd (FMD a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
decalc_fmd (a);
|
||||
a->rate = rate;
|
||||
calc_fmd (a);
|
||||
// de-emphasis filter
|
||||
impulse = 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);
|
||||
setImpulse_fircore (a->pde, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
// audio filter
|
||||
impulse = 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));
|
||||
setImpulse_fircore (a->paud, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
setSamplerate_wcpagc (a->plim, (int)a->rate);
|
||||
}
|
||||
|
||||
void setSize_fmd (FMD a, int size)
|
||||
{
|
||||
double* impulse;
|
||||
decalc_fmd (a);
|
||||
_aligned_free (a->audio);
|
||||
a->size = size;
|
||||
calc_fmd (a);
|
||||
a->audio = (double *) malloc0 (a->size * sizeof (complex));
|
||||
// de-emphasis filter
|
||||
destroy_fircore (a->pde);
|
||||
impulse = 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 = create_fircore (a->size, a->audio, a->out, a->nc_de, a->mp_de, impulse);
|
||||
_aligned_free (impulse);
|
||||
// audio filter
|
||||
destroy_fircore (a->paud);
|
||||
impulse = 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 = create_fircore (a->size, a->out, a->out, a->nc_aud, a->mp_aud, impulse);
|
||||
_aligned_free (impulse);
|
||||
setSize_wcpagc (a->plim, a->size);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXAFMDeviation (int channel, double deviation)
|
||||
{
|
||||
FMD a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].fmd.p;
|
||||
a->deviation = deviation;
|
||||
a->again = a->rate / (a->deviation * TWOPI);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXACTCSSFreq (int channel, double freq)
|
||||
{
|
||||
FMD a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].fmd.p;
|
||||
a->ctcss_freq = freq;
|
||||
SetSNCTCSSFreq (a->sntch, a->ctcss_freq);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXACTCSSRun (int channel, int run)
|
||||
{
|
||||
FMD a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].fmd.p;
|
||||
a->sntch_run = run;
|
||||
SetSNCTCSSRun (a->sntch, a->sntch_run);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMNCde (int channel, int nc)
|
||||
{
|
||||
FMD a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].fmd.p;
|
||||
if (a->nc_de != nc)
|
||||
{
|
||||
a->nc_de = nc;
|
||||
impulse = 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);
|
||||
setNc_fircore (a->pde, a->nc_de, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMMPde (int channel, int mp)
|
||||
{
|
||||
FMD a;
|
||||
a = rxa[channel].fmd.p;
|
||||
if (a->mp_de != mp)
|
||||
{
|
||||
a->mp_de = mp;
|
||||
setMp_fircore (a->pde, a->mp_de);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMNCaud (int channel, int nc)
|
||||
{
|
||||
FMD a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].fmd.p;
|
||||
if (a->nc_aud != nc)
|
||||
{
|
||||
a->nc_aud = nc;
|
||||
impulse = 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));
|
||||
setNc_fircore (a->paud, a->nc_aud, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMMPaud (int channel, int mp)
|
||||
{
|
||||
FMD a;
|
||||
a = rxa[channel].fmd.p;
|
||||
if (a->mp_aud != mp)
|
||||
{
|
||||
a->mp_aud = mp;
|
||||
setMp_fircore (a->paud, a->mp_aud);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMLimRun (int channel, int run)
|
||||
{
|
||||
FMD a;
|
||||
a = rxa[channel].fmd.p;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
if (a->lim_run != run)
|
||||
{
|
||||
a->lim_run = run;
|
||||
}
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMLimGain (int channel, double gaindB)
|
||||
{
|
||||
double gain = pow(10.0, gaindB / 20.0);
|
||||
FMD a = rxa[channel].fmd.p;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
if (a->lim_gain != gain)
|
||||
{
|
||||
decalc_fmd(a);
|
||||
a->lim_gain = gain;
|
||||
calc_fmd(a);
|
||||
}
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMAFFilter(int channel, double low, double high)
|
||||
{
|
||||
FMD a = rxa[channel].fmd.p;
|
||||
double* impulse;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
if (a->f_low != low || a->f_high != high)
|
||||
{
|
||||
a->f_low = low;
|
||||
a->f_high = high;
|
||||
// de-emphasis filter
|
||||
impulse = 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);
|
||||
setImpulse_fircore (a->pde, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
// audio filter
|
||||
impulse = 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));
|
||||
setImpulse_fircore (a->paud, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
110
fmd.h
Normal file
110
fmd.h
Normal file
@ -0,0 +1,110 @@
|
||||
/* fmd.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _fmd_h
|
||||
#define _fmd_h
|
||||
#include "iir.h"
|
||||
#include "firmin.h"
|
||||
#include "wcpAGC.h"
|
||||
typedef struct _fmd
|
||||
{
|
||||
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;
|
||||
} fmd, *FMD;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_fmd (FMD a);
|
||||
|
||||
extern void flush_fmd (FMD a);
|
||||
|
||||
extern void xfmd (FMD a);
|
||||
|
||||
extern void setBuffers_fmd (FMD a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_fmd (FMD a, int rate);
|
||||
|
||||
extern void setSize_fmd (FMD a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMDeviation (int channel, double deviation);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMNCde (int channel, int nc);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMMPde (int channel, int mp);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMNCaud (int channel, int nc);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMMPaud (int channel, int mp);
|
||||
|
||||
#endif
|
||||
232
fmmod.c
Normal file
232
fmmod.c
Normal file
@ -0,0 +1,232 @@
|
||||
/* fmmod.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2023 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 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 = (FMMOD) malloc0 (sizeof (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_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
a->p = create_fircore (a->size, a->out, a->out, a->nc, a->mp, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_fmmod (FMMOD a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_fmmod (FMMOD a)
|
||||
{
|
||||
a->tphase = 0.0;
|
||||
a->sphase = 0.0;
|
||||
}
|
||||
|
||||
void 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)
|
||||
xfircore (a->p);
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_fmmod (FMMOD a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_fmmod (a);
|
||||
setBuffers_fircore (a->p, a->out, a->out);
|
||||
}
|
||||
|
||||
void setSamplerate_fmmod (FMMOD a, int rate)
|
||||
{
|
||||
double* impulse;
|
||||
a->samplerate = rate;
|
||||
calc_fmmod (a);
|
||||
impulse = fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setSize_fmmod (FMMOD a, int size)
|
||||
{
|
||||
double* impulse;
|
||||
a->size = size;
|
||||
calc_fmmod (a);
|
||||
setSize_fircore (a->p, a->size);
|
||||
impulse = fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetTXAFMDeviation (int channel, double deviation)
|
||||
{
|
||||
FMMOD a = txa[channel].fmmod.p;
|
||||
double bp_fc = a->f_high + deviation;
|
||||
double* impulse = fir_bandpass (a->nc, -bp_fc, +bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
setImpulse_fircore (a->p, impulse, 0);
|
||||
_aligned_free (impulse);
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->deviation = deviation;
|
||||
// mod
|
||||
a->sphase = 0.0;
|
||||
a->sdelta = TWOPI * a->deviation / a->samplerate;
|
||||
// bandpass
|
||||
a->bp_fc = bp_fc;
|
||||
setUpdate_fircore (a->p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACTCSSFreq (int channel, double freq)
|
||||
{
|
||||
FMMOD a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].fmmod.p;
|
||||
a->ctcss_freq = freq;
|
||||
a->tphase = 0.0;
|
||||
a->tdelta = TWOPI * a->ctcss_freq / a->samplerate;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXACTCSSRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].fmmod.p->ctcss_run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMNC (int channel, int nc)
|
||||
{
|
||||
FMMOD a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].fmmod.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMMP (int channel, int mp)
|
||||
{
|
||||
FMMOD a;
|
||||
a = txa[channel].fmmod.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAFMAFFreqs (int channel, double low, double high)
|
||||
{
|
||||
FMMOD a;
|
||||
double* impulse;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
a = txa[channel].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_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size));
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
87
fmmod.h
Normal file
87
fmmod.h
Normal file
@ -0,0 +1,87 @@
|
||||
/* fmmod.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016, 2023 Warren Pratt, NR0V
|
||||
|
||||
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 _fmmod_h
|
||||
#define _fmmod_h
|
||||
#include "firmin.h"
|
||||
typedef struct _fmmod
|
||||
{
|
||||
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;
|
||||
}fmmod, *FMMOD;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_fmmod (FMMOD a);
|
||||
|
||||
extern void flush_fmmod (FMMOD a);
|
||||
|
||||
extern void xfmmod (FMMOD a);
|
||||
|
||||
extern void setBuffers_fmmod (FMMOD a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_fmmod (FMMOD a, int rate);
|
||||
|
||||
extern void setSize_fmmod (FMMOD a, int size);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetTXAFMDeviation (int channel, double deviation);
|
||||
|
||||
extern __declspec (dllexport) void SetTXACTCSSFreq (int channel, double freq);
|
||||
|
||||
extern __declspec (dllexport) void SetTXACTCSSRun (int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAFMMP (int channel, int mp);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAFMNC (int channel, int nc);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAFMAFFreqs (int channel, double low, double high);
|
||||
|
||||
#endif
|
||||
279
fmsq.c
Normal file
279
fmsq.c
Normal file
@ -0,0 +1,279 @@
|
||||
/* fmsq.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void calc_fmsq (FMSQ a)
|
||||
{
|
||||
double delta, theta;
|
||||
double* impulse;
|
||||
int i;
|
||||
// noise filter
|
||||
a->noise = (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 = eq_impulse (a->nc, 3, a->F, a->G, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
a->p = create_fircore (a->size, a->trigger, a->noise, a->nc, a->mp, impulse);
|
||||
_aligned_free (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 = (double *)malloc0 ((a->ntup + 1) * sizeof(double));
|
||||
a->cdown = (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 decalc_fmsq (FMSQ a)
|
||||
{
|
||||
_aligned_free(a->cdown);
|
||||
_aligned_free(a->cup);
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free(a->noise);
|
||||
}
|
||||
|
||||
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 = (FMSQ) malloc0 (sizeof (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 destroy_fmsq (FMSQ a)
|
||||
{
|
||||
decalc_fmsq (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_fmsq (FMSQ a)
|
||||
{
|
||||
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 xfmsq (FMSQ a)
|
||||
{
|
||||
if (a->run)
|
||||
{
|
||||
int i;
|
||||
double noise, lnlimit;
|
||||
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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_fmsq (FMSQ a, double* in, double* out, double* trig)
|
||||
{
|
||||
a->insig = in;
|
||||
a->outsig = out;
|
||||
a->trigger = trig;
|
||||
setBuffers_fircore (a->p, a->trigger, a->noise);
|
||||
}
|
||||
|
||||
void setSamplerate_fmsq (FMSQ a, int rate)
|
||||
{
|
||||
decalc_fmsq (a);
|
||||
a->rate = rate;
|
||||
calc_fmsq (a);
|
||||
}
|
||||
|
||||
void setSize_fmsq (FMSQ a, int size)
|
||||
{
|
||||
decalc_fmsq (a);
|
||||
a->size = size;
|
||||
calc_fmsq (a);
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXAFMSQRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].fmsq.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMSQThreshold (int channel, double threshold)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].fmsq.p->tail_thresh = threshold;
|
||||
rxa[channel].fmsq.p->unmute_thresh = 0.9 * threshold;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMSQNC (int channel, int nc)
|
||||
{
|
||||
FMSQ a;
|
||||
double* impulse;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = rxa[channel].fmsq.p;
|
||||
if (a->nc != nc)
|
||||
{
|
||||
a->nc = nc;
|
||||
impulse = eq_impulse (a->nc, 3, a->F, a->G, a->rate, 1.0 / (2.0 * a->size), 0, 0);
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAFMSQMP (int channel, int mp)
|
||||
{
|
||||
FMSQ a;
|
||||
a = rxa[channel].fmsq.p;
|
||||
if (a->mp != mp)
|
||||
{
|
||||
a->mp = mp;
|
||||
setMp_fircore (a->p, a->mp);
|
||||
}
|
||||
}
|
||||
96
fmsq.h
Normal file
96
fmsq.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* fmsq.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||
|
||||
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 _fmsq_h
|
||||
#define _fmsq_h
|
||||
#include "firmin.h"
|
||||
typedef struct _fmsq
|
||||
{
|
||||
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;
|
||||
} fmsq, *FMSQ;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_fmsq (FMSQ a);
|
||||
|
||||
extern void flush_fmsq (FMSQ a);
|
||||
|
||||
extern void xfmsq (FMSQ a);
|
||||
|
||||
extern void setBuffers_fmsq (FMSQ a, double* in, double* out, double* trig);
|
||||
|
||||
extern void setSamplerate_fmsq (FMSQ a, int rate);
|
||||
|
||||
extern void setSize_fmsq (FMSQ a, int size);
|
||||
|
||||
// RXA Properties
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMSQThreshold (int channel, double threshold);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMSQNC (int channel, int nc);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAFMSQMP (int channel, int mp);
|
||||
|
||||
#endif
|
||||
125
gain.c
Normal file
125
gain.c
Normal file
@ -0,0 +1,125 @@
|
||||
/* gain.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
PORT
|
||||
GAIN create_gain (int run, int* prun, int size, double* in, double* out, double Igain, double Qgain)
|
||||
{
|
||||
GAIN a = (GAIN) malloc0 (sizeof (gain));
|
||||
a->run = run;
|
||||
a->prun = prun;
|
||||
a->size = size;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->Igain = Igain;
|
||||
a->Qgain = Qgain;
|
||||
InitializeCriticalSectionAndSpinCount(&a->cs_update, 2500);
|
||||
return a;
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_gain (GAIN a)
|
||||
{
|
||||
DeleteCriticalSection (&a->cs_update);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
PORT
|
||||
void flush_gain (GAIN a)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PORT
|
||||
void xgain (GAIN a)
|
||||
{
|
||||
int srun;
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
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 (complex));
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
void setBuffers_gain (GAIN a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void setSamplerate_gain (GAIN a, int rate)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void setSize_gain (GAIN a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* POINTER-BASED PROPERTIES *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void pSetTXOutputLevel (GAIN a, double level)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->Igain = level;
|
||||
a->Qgain = level;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetTXOutputLevelRun (GAIN a, int run)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
|
||||
PORT
|
||||
void pSetTXOutputLevelSize (GAIN a, int size)
|
||||
{
|
||||
EnterCriticalSection (&a->cs_update);
|
||||
a->size = size;
|
||||
LeaveCriticalSection (&a->cs_update);
|
||||
}
|
||||
66
gain.h
Normal file
66
gain.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* gain.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _gain_h
|
||||
#define _gain_h
|
||||
|
||||
typedef struct _gain
|
||||
{
|
||||
int run;
|
||||
int* prun;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double Igain;
|
||||
double Qgain;
|
||||
CRITICAL_SECTION cs_update;
|
||||
}gain, *GAIN;
|
||||
|
||||
__declspec (dllexport) GAIN create_gain (int run, int* prun, int size, double* in, double* out, double Igain, double Qgain);
|
||||
|
||||
__declspec (dllexport) void destroy_gain (GAIN a);
|
||||
|
||||
__declspec (dllexport) void flush_gain (GAIN a);
|
||||
|
||||
__declspec (dllexport) void xgain (GAIN a);
|
||||
|
||||
extern void setBuffers_gain (GAIN a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_gain (GAIN a, int rate);
|
||||
|
||||
extern void setSize_gain (GAIN a, int size);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
// POINTER-BASED Properties
|
||||
|
||||
__declspec (dllexport) void pSetTXOutputLevel (GAIN a, double level);
|
||||
|
||||
__declspec (dllexport) void pSetTXOutputLevelRun (GAIN a, int run);
|
||||
|
||||
__declspec (dllexport) void pSetTXOutputLevelSize (GAIN a, int size);
|
||||
|
||||
#endif
|
||||
275
gaussian.c
Normal file
275
gaussian.c
Normal file
@ -0,0 +1,275 @@
|
||||
/* gaussian.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025-2026 Warren Pratt, NR0V
|
||||
|
||||
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 "comm.h"
|
||||
|
||||
static int calc_nc (double fwhm, double nsigma, double rate)
|
||||
{
|
||||
int nc;
|
||||
double fsigma = fwhm / 2.35482;
|
||||
double sigma = 1.0 / (2.0 * PI * fsigma);
|
||||
nc = (int)ceil (2.0 * nsigma * sigma * rate);
|
||||
nc--;
|
||||
nc |= nc >> 1;
|
||||
nc |= nc >> 2;
|
||||
nc |= nc >> 4;
|
||||
nc |= nc >> 8;
|
||||
nc |= nc >> 16;
|
||||
nc++;
|
||||
if (nc < 1024) nc = 1024;
|
||||
return nc;
|
||||
}
|
||||
|
||||
double* build_gaussian(int nc, double rate, double f, double fwhm, double scale, double nsigma)
|
||||
{
|
||||
// nc - number of impulse response values, IS EVEN FOR THE FOLLOWING CODE
|
||||
// rate - sample_rate (samples/second)
|
||||
// f - center frequency (Hz)
|
||||
// fwhm - bandwidth (Hz)
|
||||
// scale - scale factor to apply to impulse response
|
||||
// nsigma - number of sigma to extend on each side of center
|
||||
double fsigma = fwhm / 2.35482;
|
||||
double sigma = 1.0 / (2.0 * PI * fsigma);
|
||||
double* impulse = (double*)malloc0 (nc * sizeof(double));
|
||||
double delta = 1.0 / rate;
|
||||
int i, j;
|
||||
double x, y;
|
||||
double gmult = 1.0 / (sqrt (2.0 * PI) * sigma);
|
||||
double gdiv = 1.0 / (2.0 * sigma * sigma);
|
||||
double sum = 0.0;
|
||||
for (i = 0, y = -((double)(nc - 1) / 2.0); i < nc; i++, y += 1.0)
|
||||
{
|
||||
x = y * delta;
|
||||
impulse[i] = gmult * exp (-(x * x) * gdiv);
|
||||
sum += impulse[i];
|
||||
}
|
||||
for (i = 0; i < nc; i++)
|
||||
{
|
||||
impulse[i] *= (scale / sum);
|
||||
}
|
||||
// print_impulse("gaussian.txt", nc, impulse, 0, 0);
|
||||
double* c_impulse = (double*)malloc0 (nc * sizeof(complex));
|
||||
double w_osc = -2.0 * PI * f / rate;
|
||||
double m = 0.5 * (double)(nc - 1);
|
||||
double posi, posj;
|
||||
double coef;
|
||||
for (i = (nc + 1) / 2, j = nc / 2 - 1; i < nc; i++, j--)
|
||||
{
|
||||
posi = (double)i - m;
|
||||
posj = (double)j - m;
|
||||
coef = impulse[j];
|
||||
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);
|
||||
}
|
||||
// print_impulse("c_gaussian.txt", nc, c_impulse, 1, 0);
|
||||
_aligned_free (impulse);
|
||||
return c_impulse;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Gaussian *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
GAUSSIAN create_gaussian (int run, int position, int size, int nc, double* in, double* out,
|
||||
double f_center, double bandwidth, int samplerate, double gain, double nsigma, int mode)
|
||||
{
|
||||
GAUSSIAN a = (GAUSSIAN)malloc0 (sizeof(gaussian));
|
||||
double* impulse;
|
||||
a->run = run;
|
||||
a->position = position;
|
||||
a->size = size;
|
||||
a->nc = nc;
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
a->f_center = f_center;
|
||||
a->bandwidth = bandwidth;
|
||||
a->samplerate = samplerate;
|
||||
a->gain = gain;
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
a->nsigma = nsigma;
|
||||
a->mode = mode;
|
||||
a->nc_default = a->nc;
|
||||
if (a->nc == 0) a->nc = calc_nc (a->bandwidth, a->nsigma, a->samplerate);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
impulse = build_gaussian (a->nc, (double)a->samplerate, a->f_center, a->bandwidth, a->scale, a->nsigma);
|
||||
a->p = create_fircore (a->size, a->in, a->out, a->nc, 0, impulse);
|
||||
_aligned_free (impulse);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_gaussian (GAUSSIAN a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_gaussian (GAUSSIAN a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xgaussian (GAUSSIAN a, int pos)
|
||||
{
|
||||
if (a->run && a->position == pos)
|
||||
{
|
||||
// 'mode == 0' => CWL
|
||||
if (a->mode == 1) // CWU
|
||||
{
|
||||
for (int i = 0; i < a->size; i++)
|
||||
a->in[2 * i + 1] *= -1.0;
|
||||
}
|
||||
if (a->mode == 2) // CWL + CWU
|
||||
{
|
||||
for (int i = 0; i < a->size; i++)
|
||||
a->in[2 * i + 1] = a->in[2 * i + 0];
|
||||
}
|
||||
xfircore(a->p);
|
||||
}
|
||||
else if (a->out != a->in)
|
||||
memcpy (a->out, a->in, a->size * sizeof(complex));
|
||||
}
|
||||
|
||||
void setBuffers_gaussian (GAUSSIAN a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
setBuffers_fircore (a->p, a->in, a->out);
|
||||
}
|
||||
|
||||
void setSamplerate_gaussian (GAUSSIAN a, int rate)
|
||||
{
|
||||
a->samplerate = rate;
|
||||
int nc = a->nc;
|
||||
a->nc = a->nc_default;
|
||||
if (a->nc == 0) a->nc = calc_nc (a->bandwidth, a->nsigma, a->samplerate);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
double* impulse = build_gaussian (a->nc, (double)a->samplerate, a->f_center, a->bandwidth, a->scale, a->nsigma);
|
||||
if (nc == a->nc) setImpulse_fircore (a->p, impulse, 1);
|
||||
else setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setSize_gaussian (GAUSSIAN a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
setSize_fircore (a->p, a->size);
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
int nc = a->nc;
|
||||
a->nc = a->nc_default;
|
||||
if (a->nc == 0) a->nc = calc_nc (a->bandwidth, a->nsigma, a->samplerate);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
double* impulse = build_gaussian (a->nc, (double)a->samplerate, a->f_center, a->bandwidth, a->scale, a->nsigma);
|
||||
if (nc == a->nc) setImpulse_fircore (a->p, impulse, 1);
|
||||
else setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void setGain_gaussian (GAUSSIAN a, double gain)
|
||||
{
|
||||
a->gain = gain;
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
double* impulse = build_gaussian (a->nc, (double)a->samplerate, a->f_center, a->bandwidth, a->scale, a->nsigma);
|
||||
setImpulse_fircore (a->p, impulse, 1);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void CalcGaussianFilter (GAUSSIAN a, double f_center, double bandwidth, double gain)
|
||||
{
|
||||
double* impulse;
|
||||
if ((a->f_center != f_center) || (a->bandwidth != bandwidth) || (a->gain != gain))
|
||||
{
|
||||
a->f_center = f_center;
|
||||
a->bandwidth = bandwidth;
|
||||
a->gain = gain;
|
||||
a->scale = a->gain / (double)(2 * a->size);
|
||||
int nc = a->nc;
|
||||
a->nc = a->nc_default;
|
||||
if (a->nc == 0) a->nc = calc_nc (a->bandwidth, a->nsigma, a->samplerate);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
impulse = build_gaussian (a->nc, (double)a->samplerate, a->f_center, a->bandwidth, a->scale, a->nsigma);
|
||||
if (nc == a->nc) setImpulse_fircore (a->p, impulse, 1);
|
||||
else setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void SetRXAGaussianRun (int channel, int run)
|
||||
{
|
||||
GAUSSIAN a = rxa[channel].gaussian.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGaussianFreqs (int channel, double f_center, double bandwidth)
|
||||
{
|
||||
GAUSSIAN a = rxa[channel].gaussian.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
CalcGaussianFilter (a, f_center, bandwidth, a->gain);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGaussianGain (int channel, double gain)
|
||||
{
|
||||
GAUSSIAN a = rxa[channel].gaussian.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
CalcGaussianFilter (a, a->f_center, a->bandwidth, gain);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAGaussianNC (int channel, int nc)
|
||||
{
|
||||
double* impulse;
|
||||
GAUSSIAN a = rxa[channel].gaussian.p;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
if (nc != a->nc || nc == 0)
|
||||
{
|
||||
a->nc = nc;
|
||||
a->nc_default = a->nc;
|
||||
if (a->nc == 0) a->nc = calc_nc (a->bandwidth, a->nsigma, a->samplerate);
|
||||
if (a->size > a->nc) a->nc = a->size;
|
||||
impulse = build_gaussian (a->nc, (double)a->samplerate, a->f_center, a->bandwidth, a->scale, a->nsigma);
|
||||
setNc_fircore (a->p, a->nc, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
82
gaussian.h
Normal file
82
gaussian.h
Normal file
@ -0,0 +1,82 @@
|
||||
/* gaussian.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2025-2026 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Partitioned Overlap-Save Gaussian *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
|
||||
#ifndef _gaussian_h
|
||||
#define _gaussian_h
|
||||
#include "firmin.h"
|
||||
typedef struct _gaussian
|
||||
{
|
||||
int run; // 0 - filter is OFF; 1 - filter is ON
|
||||
int position; // position in sequence in which to execute the filter
|
||||
int size; // input/output buffer size
|
||||
int nc; // number of filter coefficients; if set to '0', will be automatically calculated
|
||||
int nc_default; // value to which 'nc' was last set at instantiation or by a call to set 'nc'
|
||||
double* in; // pointer to input buffer
|
||||
double* out; // pointer to output buffer
|
||||
double f_center; // filter center frequency (Hz)
|
||||
double bandwidth; // filter bandwidth (Hz)
|
||||
double samplerate; // sample_rate (samples/sec)
|
||||
double gain; // gain to be applied to filter output
|
||||
double scale; // internal filter scale factor based upon gain
|
||||
double nsigma; // number of 'sigma' on each side of center to extend the Gaussian curve
|
||||
int mode; // Mode to get output: 0 => CWL; 1 => CWU; 2 => CWL + CWU
|
||||
FIRCORE p; // pointer to partititioned overlap-save filter
|
||||
}gaussian, *GAUSSIAN;
|
||||
|
||||
extern GAUSSIAN create_gaussian(int run, int position, int size, int nc, double* in, double* out,
|
||||
double f_center, double bandwidth, int samplerate, double gain, double nsigma, int mode);
|
||||
|
||||
extern void destroy_gaussian(GAUSSIAN a);
|
||||
|
||||
extern void flush_gaussian(GAUSSIAN a);
|
||||
|
||||
extern void xgaussian(GAUSSIAN a, int pos);
|
||||
|
||||
extern void setBuffers_gaussian(GAUSSIAN a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_gaussian(GAUSSIAN a, int rate);
|
||||
|
||||
extern void setSize_gaussian(GAUSSIAN a, int size);
|
||||
|
||||
extern void setGain_gaussian(GAUSSIAN a, double gain);
|
||||
|
||||
extern void CalcGaussianFilter(GAUSSIAN a, double f_center, double bandwidth, double gain);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAGaussianRun(int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAGaussianFreqs(int channel, double f_center, double bandwidth);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAGaussianGain(int channel, double gain);
|
||||
|
||||
extern __declspec (dllexport) void SetRXAGaussianNC(int channel, int nc);
|
||||
|
||||
#endif
|
||||
969
gen.c
Normal file
969
gen.c
Normal file
@ -0,0 +1,969 @@
|
||||
/* gen.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2025 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 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 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 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 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 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 = (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 calc_ttpulse(GEN a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
a->ttpulse.pperiod = 1.0 / a->ttpulse.pf;
|
||||
a->ttpulse.tphs1 = 0.0;
|
||||
a->ttpulse.tphs2 = 0.0;
|
||||
a->ttpulse.tdelta1 = TWOPI * a->ttpulse.tf1 / a->rate;
|
||||
a->ttpulse.tdelta2 = TWOPI * a->ttpulse.tf2 / a->rate;
|
||||
a->ttpulse.tcosdelta1 = cos(a->ttpulse.tdelta1);
|
||||
a->ttpulse.tcosdelta2 = cos(a->ttpulse.tdelta2);
|
||||
a->ttpulse.tsindelta1 = sin(a->ttpulse.tdelta1);
|
||||
a->ttpulse.tsindelta2 = sin(a->ttpulse.tdelta2);
|
||||
a->ttpulse.pntrans = (int)(a->ttpulse.ptranstime * a->rate);
|
||||
a->ttpulse.pnon = (int)(a->ttpulse.pdutycycle * a->ttpulse.pperiod * a->rate);
|
||||
a->ttpulse.pnoff = (int)(a->ttpulse.pperiod * a->rate) - a->ttpulse.pnon - 2 * a->ttpulse.pntrans;
|
||||
if (a->ttpulse.pnoff < 0) a->ttpulse.pnoff = 0;
|
||||
a->ttpulse.pcount = a->ttpulse.pnoff;
|
||||
a->ttpulse.state = 0;
|
||||
a->ttpulse.ctrans = (double*)malloc0((a->ttpulse.pntrans + 1) * sizeof(double));
|
||||
delta = PI / (double)a->ttpulse.pntrans;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->ttpulse.pntrans; i++)
|
||||
{
|
||||
a->ttpulse.ctrans[i] = 0.5 * (1.0 - cos(theta));
|
||||
theta += delta;
|
||||
}
|
||||
}
|
||||
|
||||
void calc_gen (GEN a)
|
||||
{
|
||||
calc_tone (a);
|
||||
calc_tt (a);
|
||||
calc_sweep (a);
|
||||
calc_sawtooth (a);
|
||||
calc_triangle (a);
|
||||
calc_pulse (a);
|
||||
calc_ttpulse (a);
|
||||
}
|
||||
|
||||
void decalc_gen (GEN a)
|
||||
{
|
||||
_aligned_free (a->ttpulse.ctrans);
|
||||
_aligned_free (a->pulse.ctrans);
|
||||
}
|
||||
|
||||
GEN create_gen (int run, int size, double* in, double* out, int rate, int mode)
|
||||
{
|
||||
GEN a = (GEN) malloc0 (sizeof (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 = 2.0;
|
||||
a->pulse.pdutycycle = 0.25;
|
||||
a->pulse.ptranstime = 0.005;
|
||||
a->pulse.tf = 600.0;
|
||||
a->pulse.IQout = 0;
|
||||
// two-tone pulse
|
||||
a->ttpulse.mag1 = 0.5;
|
||||
a->ttpulse.mag2 = 0.5;
|
||||
a->ttpulse.pf = 2.0;
|
||||
a->ttpulse.pdutycycle = 0.25;
|
||||
a->ttpulse.ptranstime = 0.005;
|
||||
a->ttpulse.tf1 = 900.0;
|
||||
a->ttpulse.tf2 = 1700.0;
|
||||
a->ttpulse.IQout = 0;
|
||||
calc_gen (a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_gen (GEN a)
|
||||
{
|
||||
decalc_gen (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_gen (GEN a)
|
||||
{
|
||||
a->pulse.state = 0;
|
||||
a->ttpulse.state = 0;
|
||||
}
|
||||
|
||||
enum pstate
|
||||
{
|
||||
OFF,
|
||||
UP,
|
||||
ON,
|
||||
DOWN
|
||||
};
|
||||
|
||||
void 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 or IQ output)
|
||||
{
|
||||
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;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = UP;
|
||||
a->pulse.pcount = a->pulse.pntrans;
|
||||
}
|
||||
break;
|
||||
case UP:
|
||||
|
||||
if (a->pulse.IQout)
|
||||
{
|
||||
a->out[2 * i + 0] = +a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pntrans - a->pulse.pcount];
|
||||
a->out[2 * i + 1] = -a->pulse.mag * sinphase * a->pulse.ctrans[a->pulse.pntrans - a->pulse.pcount];
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = +a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pntrans - a->pulse.pcount];
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = ON;
|
||||
a->pulse.pcount = a->pulse.pnon;
|
||||
}
|
||||
break;
|
||||
case ON:
|
||||
|
||||
if (a->pulse.IQout)
|
||||
{
|
||||
a->out[2 * i + 0] = +a->pulse.mag * cosphase;
|
||||
a->out[2 * i + 1] = -a->pulse.mag * sinphase;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = +a->pulse.mag * cosphase;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
if (--a->pulse.pcount == 0)
|
||||
{
|
||||
a->pulse.state = DOWN;
|
||||
a->pulse.pcount = a->pulse.pntrans;
|
||||
}
|
||||
break;
|
||||
case DOWN:
|
||||
if (a->pulse.IQout)
|
||||
{
|
||||
a->out[2 * i + 0] = +a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pcount];
|
||||
a->out[2 * i + 1] = -a->pulse.mag * sinphase * a->pulse.ctrans[a->pulse.pcount];
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = +a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pcount];
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
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;
|
||||
case 7: // two-tone pulse (audio or IQ)
|
||||
{
|
||||
int i;
|
||||
double t1a, t1b, t2a, t2b;
|
||||
double cosphase1 = cos(a->ttpulse.tphs1);
|
||||
double cosphase2 = cos(a->ttpulse.tphs2);
|
||||
double sinphase1 = sin(a->ttpulse.tphs1);
|
||||
double sinphase2 = sin(a->ttpulse.tphs2);
|
||||
for (i = 0; i < a->size; i++)
|
||||
{
|
||||
if (a->ttpulse.pnoff != 0)
|
||||
{
|
||||
switch (a->ttpulse.state)
|
||||
{
|
||||
case OFF:
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
if (--a->ttpulse.pcount == 0)
|
||||
{
|
||||
a->ttpulse.state = UP;
|
||||
a->ttpulse.pcount = a->ttpulse.pntrans;
|
||||
}
|
||||
break;
|
||||
case UP:
|
||||
if (a->ttpulse.IQout)
|
||||
{
|
||||
a->out[2 * i + 0] = +(a->ttpulse.mag1 * cosphase1 + a->ttpulse.mag2 * cosphase2) * a->ttpulse.ctrans[a->ttpulse.pntrans - a->ttpulse.pcount];
|
||||
a->out[2 * i + 1] = -(a->ttpulse.mag1 * sinphase1 + a->ttpulse.mag2 * sinphase2) * a->ttpulse.ctrans[a->ttpulse.pntrans - a->ttpulse.pcount];
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = +(a->ttpulse.mag1 * cosphase1 + a->ttpulse.mag2 * cosphase2) * a->ttpulse.ctrans[a->ttpulse.pntrans - a->ttpulse.pcount];
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
if (--a->ttpulse.pcount == 0)
|
||||
{
|
||||
a->ttpulse.state = ON;
|
||||
a->ttpulse.pcount = a->ttpulse.pnon;
|
||||
}
|
||||
break;
|
||||
case ON:
|
||||
if (a->ttpulse.IQout)
|
||||
{
|
||||
a->out[2 * i + 0] = +(a->ttpulse.mag1 * cosphase1 + a->ttpulse.mag2 * cosphase2);
|
||||
a->out[2 * i + 1] = -(a->ttpulse.mag1 * sinphase1 + a->ttpulse.mag2 * sinphase2);
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = +(a->ttpulse.mag1 * cosphase1 + a->ttpulse.mag2 * cosphase2);
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
if (--a->ttpulse.pcount == 0)
|
||||
{
|
||||
a->ttpulse.state = DOWN;
|
||||
a->ttpulse.pcount = a->ttpulse.pntrans;
|
||||
}
|
||||
break;
|
||||
case DOWN:
|
||||
if (a->ttpulse.IQout)
|
||||
{
|
||||
a->out[2 * i + 0] = +(a->ttpulse.mag1 * cosphase1 + a->ttpulse.mag2 * cosphase2) * a->ttpulse.ctrans[a->ttpulse.pcount];
|
||||
a->out[2 * i + 1] = -(a->ttpulse.mag1 * sinphase1 + a->ttpulse.mag2 * sinphase2) * a->ttpulse.ctrans[a->ttpulse.pcount];
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = +(a->ttpulse.mag1 * cosphase1 + a->ttpulse.mag2 * cosphase2) * a->ttpulse.ctrans[a->ttpulse.pcount];
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
if (--a->ttpulse.pcount == 0)
|
||||
{
|
||||
a->ttpulse.state = OFF;
|
||||
a->ttpulse.pcount = a->ttpulse.pnoff;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a->out[2 * i + 0] = 0.0;
|
||||
a->out[2 * i + 1] = 0.0;
|
||||
}
|
||||
t1a = cosphase1;
|
||||
t1b = sinphase1;
|
||||
cosphase1 = t1a * a->ttpulse.tcosdelta1 - t1b * a->ttpulse.tsindelta1;
|
||||
sinphase1 = t1a * a->ttpulse.tsindelta1 + t1b * a->ttpulse.tcosdelta1;
|
||||
a->ttpulse.tphs1 += a->ttpulse.tdelta1;
|
||||
if (a->ttpulse.tphs1 >= TWOPI) a->ttpulse.tphs1 -= TWOPI;
|
||||
if (a->ttpulse.tphs1 < 0.0) a->ttpulse.tphs1 += TWOPI;
|
||||
t2a = cosphase2;
|
||||
t2b = sinphase2;
|
||||
cosphase2 = t2a * a->ttpulse.tcosdelta2 - t2b * a->ttpulse.tsindelta2;
|
||||
sinphase2 = t2a * a->ttpulse.tsindelta2 + t2b * a->ttpulse.tcosdelta2;
|
||||
a->ttpulse.tphs2 += a->ttpulse.tdelta2;
|
||||
if (a->ttpulse.tphs2 >= TWOPI) a->ttpulse.tphs2 -= TWOPI;
|
||||
if (a->ttpulse.tphs2 < 0.0) a->ttpulse.tphs2 += TWOPI;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // silence
|
||||
{
|
||||
memset (a->out, 0, a->size * sizeof (complex));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_gen (GEN a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void setSamplerate_gen (GEN a, int rate)
|
||||
{
|
||||
decalc_gen (a);
|
||||
a->rate = rate;
|
||||
calc_gen (a);
|
||||
}
|
||||
|
||||
void setSize_gen (GEN a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
flush_gen (a);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* RXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// 'PreGen', gen0
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenMode (int channel, int mode)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->mode = mode;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenToneMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->tone.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenToneFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->tone.freq = freq;
|
||||
calc_tone (rxa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenNoiseMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->noise.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenSweepMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->sweep.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenSweepFreq (int channel, double freq1, double freq2)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->sweep.f1 = freq1;
|
||||
rxa[channel].gen0.p->sweep.f2 = freq2;
|
||||
calc_sweep (rxa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetRXAPreGenSweepRate (int channel, double rate)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
rxa[channel].gen0.p->sweep.sweeprate = rate;
|
||||
calc_sweep (rxa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
// 'PreGen', gen0
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenMode (int channel, int mode)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->mode = mode;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenToneMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->tone.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenToneFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->tone.freq = freq;
|
||||
calc_tone (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenNoiseMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->noise.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenSweepMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->sweep.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenSweepFreq (int channel, double freq1, double freq2)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->sweep.f1 = freq1;
|
||||
txa[channel].gen0.p->sweep.f2 = freq2;
|
||||
calc_sweep (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenSweepRate (int channel, double rate)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->sweep.sweeprate = rate;
|
||||
calc_sweep (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenSawtoothMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->saw.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenSawtoothFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->saw.f = freq;
|
||||
calc_sawtooth (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenTriangleMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->tri.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenTriangleFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->tri.f = freq;
|
||||
calc_triangle (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenPulseMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->pulse.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenPulseFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->pulse.pf = freq;
|
||||
calc_pulse (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenPulseDutyCycle (int channel, double dc)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->pulse.pdutycycle = dc;
|
||||
calc_pulse (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenPulseToneFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->pulse.tf = freq;
|
||||
calc_pulse (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPreGenPulseTransition (int channel, double transtime)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen0.p->pulse.ptranstime = transtime;
|
||||
calc_pulse (txa[channel].gen0.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
// 'PostGen', gen1
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenRun (int channel, int run)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->run = run;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenMode (int channel, int mode)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->mode = mode;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenToneMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->tone.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenToneFreq (int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->tone.freq = freq;
|
||||
calc_tone (txa[channel].gen1.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTMag (int channel, double mag1, double mag2)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->tt.mag1 = mag1;
|
||||
txa[channel].gen1.p->tt.mag2 = mag2;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTFreq (int channel, double freq1, double freq2)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->tt.f1 = freq1;
|
||||
txa[channel].gen1.p->tt.f2 = freq2;
|
||||
calc_tt (txa[channel].gen1.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenSweepMag (int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->sweep.mag = mag;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenSweepFreq (int channel, double freq1, double freq2)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->sweep.f1 = freq1;
|
||||
txa[channel].gen1.p->sweep.f2 = freq2;
|
||||
calc_sweep (txa[channel].gen1.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenSweepRate (int channel, double rate)
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->sweep.sweeprate = rate;
|
||||
calc_sweep (txa[channel].gen1.p);
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenPulseMag(int channel, double mag)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->pulse.mag = mag;
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenPulseFreq(int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->pulse.pf = freq;
|
||||
calc_pulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenPulseDutyCycle(int channel, double dc)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->pulse.pdutycycle = dc;
|
||||
calc_pulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenPulseToneFreq(int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->pulse.tf = freq;
|
||||
calc_pulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenPulseTransition(int channel, double transtime)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->pulse.ptranstime = transtime;
|
||||
calc_pulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenPulseIQout(int channel, int IQout)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->pulse.IQout = IQout;
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTPulseMag(int channel, double mag1, double mag2)
|
||||
{
|
||||
// defaults are 0.5/0.5
|
||||
// total must not exceed 1.0
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->ttpulse.mag1 = mag1;
|
||||
txa[channel].gen1.p->ttpulse.mag2 = mag2;
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTPulseFreq(int channel, double freq)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->ttpulse.pf = freq;
|
||||
calc_ttpulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTPulseDutyCycle(int channel, double dc)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->ttpulse.pdutycycle = dc;
|
||||
calc_ttpulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTPulseToneFreq(int channel, double freq1, double freq2)
|
||||
{
|
||||
GEN a = txa[channel].gen1.p;
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
a->ttpulse.tf1 = freq1;
|
||||
a->ttpulse.tf2 = freq2;
|
||||
calc_ttpulse(a);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTPulseTransition(int channel, double transtime)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->ttpulse.ptranstime = transtime;
|
||||
calc_ttpulse(txa[channel].gen1.p);
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAPostGenTTPulseIQout(int channel, int IQout)
|
||||
{
|
||||
EnterCriticalSection(&ch[channel].csDSP);
|
||||
txa[channel].gen1.p->ttpulse.IQout = IQout;
|
||||
LeaveCriticalSection(&ch[channel].csDSP);
|
||||
}
|
||||
160
gen.h
Normal file
160
gen.h
Normal file
@ -0,0 +1,160 @@
|
||||
/* gen.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2025 Warren Pratt, NR0V
|
||||
|
||||
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 _gen_h
|
||||
#define _gen_h
|
||||
|
||||
typedef struct _gen
|
||||
{
|
||||
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;
|
||||
int IQout;
|
||||
} pulse;
|
||||
struct _ttpulse
|
||||
{
|
||||
double mag1;
|
||||
double mag2;
|
||||
double pf;
|
||||
double pdutycycle;
|
||||
double ptranstime;
|
||||
double* ctrans;
|
||||
int pcount;
|
||||
int pnon;
|
||||
int pntrans;
|
||||
int pnoff;
|
||||
double pperiod;
|
||||
double tf1;
|
||||
double tf2;
|
||||
double tphs1;
|
||||
double tphs2;
|
||||
double tdelta1;
|
||||
double tdelta2;
|
||||
double tcosdelta1;
|
||||
double tcosdelta2;
|
||||
double tsindelta1;
|
||||
double tsindelta2;
|
||||
int state;
|
||||
int IQout;
|
||||
} ttpulse;
|
||||
} gen, *GEN;
|
||||
|
||||
extern GEN create_gen (int run, int size, double* in, double* out, int rate, int mode);
|
||||
|
||||
extern void destroy_gen (GEN a);
|
||||
|
||||
extern void flush_gen (GEN a);
|
||||
|
||||
extern void xgen (GEN a);
|
||||
|
||||
extern void setBuffers_gen (GEN a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_gen (GEN a, int rate);
|
||||
|
||||
extern void setSize_gen (GEN a, int size);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
|
||||
#endif
|
||||
221
icfir.c
Normal file
221
icfir.c
Normal file
@ -0,0 +1,221 @@
|
||||
/* icfir.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2018 Warren Pratt, NR0V
|
||||
|
||||
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_DEPRECATE
|
||||
#include "comm.h"
|
||||
|
||||
void 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 = create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse);
|
||||
_aligned_free (impulse);
|
||||
}
|
||||
|
||||
void decalc_icfir (ICFIR a)
|
||||
{
|
||||
destroy_fircore (a->p);
|
||||
}
|
||||
|
||||
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 = (ICFIR) malloc0 (sizeof (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 destroy_icfir (ICFIR a)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_icfir (ICFIR a)
|
||||
{
|
||||
flush_fircore (a->p);
|
||||
}
|
||||
|
||||
void xicfir (ICFIR a)
|
||||
{
|
||||
if (a->run)
|
||||
xfircore (a->p);
|
||||
else if (a->in != a->out)
|
||||
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||
}
|
||||
|
||||
void setBuffers_icfir (ICFIR a, double* in, double* out)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
void setSamplerate_icfir (ICFIR a, int rate)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->runrate = rate;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
void setSize_icfir (ICFIR a, int size)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->size = size;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
void setOutRate_icfir (ICFIR a, int rate)
|
||||
{
|
||||
decalc_icfir (a);
|
||||
a->cicrate = rate;
|
||||
calc_icfir (a);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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, fn, mag = 1.0;
|
||||
double* impulse;
|
||||
double* A = (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 = (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_fsamp (N, A, rtype, 1.0, wintype);
|
||||
// print_impulse ("icfirImpulse.txt", N, impulse, 1, 0);
|
||||
_aligned_free (A);
|
||||
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);
|
||||
//}
|
||||
71
icfir.h
Normal file
71
icfir.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* icfir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2018 Warren Pratt, NR0V
|
||||
|
||||
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 _icfir_h
|
||||
#define _icfir_h
|
||||
#include "firmin.h"
|
||||
typedef struct _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;
|
||||
double scale;
|
||||
int xtype;
|
||||
double xbw;
|
||||
int wintype;
|
||||
FIRCORE p;
|
||||
} icfir, *ICFIR;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_icfir (ICFIR a);
|
||||
|
||||
extern void flush_icfir (ICFIR a);
|
||||
|
||||
extern void xicfir (ICFIR a);
|
||||
|
||||
extern void setBuffers_icfir (ICFIR a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_icfir (ICFIR a, int rate);
|
||||
|
||||
extern void setSize_icfir (ICFIR a, int size);
|
||||
|
||||
extern void setOutRate_icfir (ICFIR a, int rate);
|
||||
|
||||
extern 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);
|
||||
|
||||
#endif
|
||||
401
iir.h
Normal file
401
iir.h
Normal file
@ -0,0 +1,401 @@
|
||||
/* iir.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V
|
||||
|
||||
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 _snotch_h
|
||||
#define _snotch_h
|
||||
|
||||
typedef struct _snotch
|
||||
{
|
||||
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;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} snotch, *SNOTCH;
|
||||
|
||||
extern SNOTCH create_snotch (int run, int size, double* in, double* out, int rate, double f, double bw);
|
||||
|
||||
extern void destroy_snotch (SNOTCH a);
|
||||
|
||||
extern void flush_snotch (SNOTCH a);
|
||||
|
||||
extern void xsnotch (SNOTCH a);
|
||||
|
||||
extern void setBuffers_snotch (SNOTCH a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_snotch (SNOTCH a, int rate);
|
||||
|
||||
extern void setSize_snotch (SNOTCH a, int size);
|
||||
|
||||
extern void SetSNCTCSSFreq (SNOTCH a, double freq);
|
||||
|
||||
extern void SetSNCTCSSRun (SNOTCH a, int run);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Bi-Quad Peaking *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _speak_h
|
||||
#define _speak_h
|
||||
|
||||
typedef struct _speak
|
||||
{
|
||||
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;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} speak, *SPEAK;
|
||||
|
||||
extern SPEAK create_speak (int run, int size, double* in, double* out, int rate, double f, double bw, double gain, int nstages, int design);
|
||||
|
||||
extern void destroy_speak (SPEAK a);
|
||||
|
||||
extern void flush_speak (SPEAK a);
|
||||
|
||||
extern void xspeak (SPEAK a);
|
||||
|
||||
extern void setBuffers_speak (SPEAK a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_speak (SPEAK a, int rate);
|
||||
|
||||
extern void setSize_speak (SPEAK a, int size);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABiQuadRun(int channel, int run);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABiQuadFreq(int channel, double freq);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABiQuadBandwidth(int channel, double bw);
|
||||
|
||||
extern __declspec (dllexport) void SetRXABiQuadGain(int channel, double gain);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Multiple Peaking *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _mpeak_h
|
||||
#define _mpeak_h
|
||||
|
||||
typedef struct _mpeak
|
||||
{
|
||||
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;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} mpeak, *MPEAK;
|
||||
|
||||
extern 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);
|
||||
|
||||
extern void destroy_mpeak (MPEAK a);
|
||||
|
||||
extern void flush_mpeak (MPEAK a);
|
||||
|
||||
extern void xmpeak (MPEAK a);
|
||||
|
||||
extern void setBuffers_mpeak (MPEAK a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_mpeak (MPEAK a, int rate);
|
||||
|
||||
extern void setSize_mpeak (MPEAK a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Phase Rotator *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _phrot_h
|
||||
#define _phrot_h
|
||||
|
||||
typedef struct _phrot
|
||||
{
|
||||
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;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} phrot, *PHROT;
|
||||
|
||||
extern PHROT create_phrot (int run, int size, double* in, double* out, int rate, double fc, int nstages);
|
||||
|
||||
extern void destroy_phrot (PHROT a);
|
||||
|
||||
extern void flush_phrot (PHROT a);
|
||||
|
||||
extern void xphrot (PHROT a);
|
||||
|
||||
extern void setBuffers_phrot (PHROT a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_phrot (PHROT a, int rate);
|
||||
|
||||
extern void setSize_phrot (PHROT a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Bi-Quad Low-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _bqlp_h
|
||||
#define _bqlp_h
|
||||
|
||||
typedef struct _bqlp
|
||||
{
|
||||
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;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} bqlp, *BQLP;
|
||||
|
||||
extern BQLP create_bqlp(int run, int size, double* in, double* out, double rate, double fc, double Q, double gain, int nstages);
|
||||
|
||||
extern void destroy_bqlp(BQLP a);
|
||||
|
||||
extern void flush_bqlp(BQLP a);
|
||||
|
||||
extern void xbqlp(BQLP a);
|
||||
|
||||
extern void setBuffers_bqlp(BQLP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_bqlp(BQLP a, int rate);
|
||||
|
||||
extern void setSize_bqlp(BQLP a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Double Bi-Quad Low-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _dbqlp_h
|
||||
#define _dbqlp_h
|
||||
|
||||
extern BQLP create_dbqlp(int run, int size, double* in, double* out, double rate, double fc, double Q, double gain, int nstages);
|
||||
|
||||
extern void destroy_dbqlp(BQLP a);
|
||||
|
||||
extern void flush_dbqlp(BQLP a);
|
||||
|
||||
extern void xdbqlp(BQLP a);
|
||||
|
||||
extern void setBuffers_dbqlp(BQLP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_dbqlp(BQLP a, int rate);
|
||||
|
||||
extern void setSize_dbqlp(BQLP a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Bi-Quad Band-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _bqbp_h
|
||||
#define _bqbp_h
|
||||
|
||||
typedef struct _bqbp
|
||||
{
|
||||
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;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} bqbp, * BQBP;
|
||||
|
||||
extern BQBP create_bqbp(int run, int size, double* in, double* out, double rate, double f_low, double f_high, double gain, int nstages);
|
||||
|
||||
extern void destroy_bqbp(BQBP a);
|
||||
|
||||
extern void flush_bqbp(BQBP a);
|
||||
|
||||
extern void xbqbp(BQBP a);
|
||||
|
||||
extern void setBuffers_bqbp(BQBP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_bqbp(BQBP a, int rate);
|
||||
|
||||
extern void setSize_bqbp(BQBP a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Double Bi-Quad Band-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _dbqbp_h
|
||||
#define _dbqbp_h
|
||||
|
||||
extern BQBP create_dbqbp(int run, int size, double* in, double* out, double rate, double f_low, double f_high, double gain, int nstages);
|
||||
|
||||
extern void destroy_dbqbp(BQBP a);
|
||||
|
||||
extern void flush_dbqbp(BQBP a);
|
||||
|
||||
extern void xdbqbp(BQBP a);
|
||||
|
||||
extern void setBuffers_dbqbp(BQBP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_dbqbp(BQBP a, int rate);
|
||||
|
||||
extern void setSize_dbqbp(BQBP a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Double Single-Pole High-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _dsphp_h
|
||||
#define _dsphp_h
|
||||
|
||||
typedef struct _sphp
|
||||
{
|
||||
int run;
|
||||
int size;
|
||||
double* in;
|
||||
double* out;
|
||||
double rate;
|
||||
double fc;
|
||||
int nstages;
|
||||
double a1, b0, b1;
|
||||
double* x0, * x1, * y0, * y1;
|
||||
CRITICAL_SECTION cs_update;
|
||||
} sphp, * SPHP;
|
||||
|
||||
extern SPHP create_dsphp(int run, int size, double* in, double* out, double rate, double fc, int nstages);
|
||||
|
||||
extern void destroy_dsphp(SPHP a);
|
||||
|
||||
extern void flush_dsphp(SPHP a);
|
||||
|
||||
extern void xdsphp(SPHP a);
|
||||
|
||||
extern void setBuffers_dsphp(SPHP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_dsphp(SPHP a, int rate);
|
||||
|
||||
extern void setSize_dsphp(SPHP a, int size);
|
||||
|
||||
#endif
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Complex Single-Pole High-Pass *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#ifndef _dphp_h
|
||||
#define _dphp_h
|
||||
|
||||
extern SPHP create_sphp(int run, int size, double* in, double* out, double rate, double fc, int nstages);
|
||||
|
||||
extern void destroy_sphp(SPHP a);
|
||||
|
||||
extern void flush_sphp(SPHP a);
|
||||
|
||||
extern void xsphp(SPHP a);
|
||||
|
||||
extern void setBuffers_sphp(SPHP a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_sphp(SPHP a, int rate);
|
||||
|
||||
extern void setSize_sphp(SPHP a, int size);
|
||||
|
||||
#endif
|
||||
290
impulse_cache.c
Normal file
290
impulse_cache.c
Normal file
@ -0,0 +1,290 @@
|
||||
/* impulse_cache.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2019, 2024 Warren Pratt, NR0V
|
||||
Copyright (C) 2025 Richard Samphire, MW0LGE
|
||||
|
||||
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
|
||||
mw0lge@grange-lane.co.uk
|
||||
|
||||
*/
|
||||
//
|
||||
//============================================================================================//
|
||||
// Dual-Licensing Statement (Applies Only to Author's Contributions, Richard Samphire MW0LGE) //
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// For any code originally written by Richard Samphire MW0LGE, or for any modifications //
|
||||
// made by him, the copyright holder for those portions (Richard Samphire) reserves the //
|
||||
// right to use, license, and distribute such code under different terms, including //
|
||||
// closed-source and proprietary licences, in addition to the GNU General Public License //
|
||||
// granted above. Nothing in this statement restricts any rights granted to recipients under //
|
||||
// the GNU GPL. Code contributed by others (not Richard Samphire) remains licensed under //
|
||||
// its original terms and is not affected by this dual-licensing statement in any way. //
|
||||
// Richard Samphire can be reached by email at : mw0lge@grange-lane.co.uk //
|
||||
//============================================================================================//
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include "comm.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Impulse Cache implementation *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#if defined(_WIN64)
|
||||
static const uint64_t FNV_OFFSET_BASIS_64 = 14695981039346656037ULL; // 0xcbf29ce484222325
|
||||
static const uint64_t FNV_PRIME_64 = 1099511628211ULL; // 0x100000001b3
|
||||
|
||||
uint64_t fnv1a_hash64(const void* data, size_t len) {
|
||||
const uint8_t* bytes = (const uint8_t*)data;
|
||||
uint64_t hash = FNV_OFFSET_BASIS_64;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
hash ^= bytes[i];
|
||||
hash *= FNV_PRIME_64;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
#else
|
||||
static const uint32_t FNV_OFFSET_BASIS_32 = 2166136261U; // 0x811C9DC5
|
||||
static const uint32_t FNV_PRIME_32 = 16777619U; // 0x01000193
|
||||
|
||||
uint32_t fnv1a_hash32(const void* data, size_t len) {
|
||||
const uint8_t* bytes = (const uint8_t*)data;
|
||||
uint32_t hash = FNV_OFFSET_BASIS_32;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
hash ^= bytes[i];
|
||||
hash *= FNV_PRIME_32;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct _cache_entry {
|
||||
HASH_T hash;
|
||||
int N; // N complex entries in impulse. Leave as signed int as that is used everywhere
|
||||
double* impulse;
|
||||
struct _cache_entry* next;
|
||||
} cache_entry;
|
||||
|
||||
static size_t _cache_counts[CACHE_BUCKETS] = { 0 };
|
||||
static cache_entry* _cache_heads[CACHE_BUCKETS] = { NULL };
|
||||
static CRITICAL_SECTION _cs_use_cache;
|
||||
static int _run = 0;
|
||||
static int _use_cache = 1;
|
||||
|
||||
void remove_impulse_cache_tail(size_t bucket)
|
||||
{
|
||||
if (bucket >= CACHE_BUCKETS) return;
|
||||
|
||||
cache_entry** pp = &_cache_heads[bucket];
|
||||
|
||||
while (*pp && (*pp)->next)
|
||||
{
|
||||
pp = &(*pp)->next;
|
||||
}
|
||||
|
||||
if (*pp)
|
||||
{
|
||||
_aligned_free((*pp)->impulse);
|
||||
_aligned_free(*pp);
|
||||
*pp = NULL;
|
||||
_cache_counts[bucket]--;
|
||||
}
|
||||
}
|
||||
|
||||
void free_impulse_cache(void)
|
||||
{
|
||||
for (size_t b = 0; b < CACHE_BUCKETS; ++b) {
|
||||
cache_entry* e = _cache_heads[b];
|
||||
while (e) {
|
||||
cache_entry* next = e->next;
|
||||
_aligned_free(e->impulse);
|
||||
_aligned_free(e);
|
||||
e = next;
|
||||
}
|
||||
_cache_heads[b] = NULL;
|
||||
_cache_counts[b] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
double* get_impulse_cache_entry(size_t bucket, HASH_T hash, int N)
|
||||
{
|
||||
if (!_run) return NULL;
|
||||
|
||||
int use;
|
||||
EnterCriticalSection(&_cs_use_cache);
|
||||
use = _use_cache;
|
||||
LeaveCriticalSection(&_cs_use_cache);
|
||||
|
||||
if (!use || bucket >= CACHE_BUCKETS) return NULL;
|
||||
|
||||
// lru, least recently used, moves cache hit to head
|
||||
// old cache entries will move towards the tail and eventually be dumped
|
||||
cache_entry* prev = NULL;
|
||||
cache_entry* e = _cache_heads[bucket];
|
||||
|
||||
while (e) {
|
||||
if (e->hash == hash && e->N == N)
|
||||
{
|
||||
if (prev)
|
||||
{
|
||||
prev->next = e->next;
|
||||
e->next = _cache_heads[bucket];
|
||||
_cache_heads[bucket] = e;
|
||||
}
|
||||
double* imp = (double*) malloc0(e->N * sizeof(complex));
|
||||
memcpy(imp, e->impulse, e->N * sizeof(complex));
|
||||
return imp;
|
||||
}
|
||||
prev = e;
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void add_impulse_to_cache(size_t bucket, HASH_T hash, int N, double* impulse)
|
||||
{
|
||||
if (!_run) return;
|
||||
|
||||
int use;
|
||||
EnterCriticalSection(&_cs_use_cache);
|
||||
use = _use_cache;
|
||||
LeaveCriticalSection(&_cs_use_cache);
|
||||
|
||||
if (!use || bucket >= CACHE_BUCKETS) return;
|
||||
|
||||
if (_cache_counts[bucket] >= MAX_CACHE_ENTRIES) remove_impulse_cache_tail(bucket);
|
||||
|
||||
cache_entry* e = malloc0(sizeof(cache_entry));
|
||||
e->hash = hash;
|
||||
e->N = N;
|
||||
e->impulse = (double *) malloc0(N * sizeof(complex));
|
||||
memcpy(e->impulse, impulse, N * sizeof(complex));
|
||||
e->next = _cache_heads[bucket];
|
||||
_cache_heads[bucket] = e;
|
||||
_cache_counts[bucket]++;
|
||||
}
|
||||
|
||||
PORT
|
||||
int save_impulse_cache(const char* path)
|
||||
{
|
||||
if (!_run) return 0;
|
||||
|
||||
int use;
|
||||
EnterCriticalSection(&_cs_use_cache);
|
||||
use = _use_cache;
|
||||
LeaveCriticalSection(&_cs_use_cache);
|
||||
if (!use) return 0;
|
||||
|
||||
FILE* fp = fopen(path, "wb");
|
||||
if (!fp) return -1;
|
||||
uint32_t buckets = CACHE_BUCKETS;
|
||||
if (fwrite(&buckets, sizeof(buckets), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
for (size_t b = 0; b < CACHE_BUCKETS; b++) {
|
||||
uint32_t count = 0;
|
||||
for (cache_entry* e = _cache_heads[b]; e; e = e->next) count++;
|
||||
if (fwrite(&count, sizeof(count), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
for (cache_entry* e = _cache_heads[b]; e; e = e->next) {
|
||||
if (fwrite(&e->hash, sizeof(HASH_T), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
if (fwrite(&e->N, sizeof(e->N), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
if (fwrite(e->impulse, sizeof(complex), e->N, fp) != (size_t)e->N) { fclose(fp); return -1; }
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PORT
|
||||
int read_impulse_cache(const char* path)
|
||||
{
|
||||
if (!_run) return 0;
|
||||
|
||||
free_impulse_cache();
|
||||
|
||||
int use;
|
||||
EnterCriticalSection(&_cs_use_cache);
|
||||
use = _use_cache;
|
||||
LeaveCriticalSection(&_cs_use_cache);
|
||||
if (!use) return 0;
|
||||
|
||||
FILE* fp = fopen(path, "rb");
|
||||
if (!fp) return -1;
|
||||
uint32_t buckets;
|
||||
if (fread(&buckets, sizeof(buckets), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
if (buckets != CACHE_BUCKETS) { fclose(fp); return -1; }
|
||||
for (size_t b = 0; b < buckets; b++) {
|
||||
uint32_t count;
|
||||
if (fread(&count, sizeof(count), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
cache_entry* tail = NULL;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
HASH_T hash;
|
||||
int N;
|
||||
if (fread(&hash, sizeof(HASH_T), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
if (fread(&N, sizeof(N), 1, fp) != 1) { fclose(fp); return -1; }
|
||||
double* data = (double*)malloc0(N * sizeof(complex));
|
||||
if (fread(data, sizeof(complex), N, fp) != (size_t)N) { _aligned_free(data); fclose(fp); return -1; }
|
||||
cache_entry* e = (cache_entry*)malloc0(sizeof(cache_entry));
|
||||
e->hash = hash;
|
||||
e->N = N;
|
||||
e->impulse = data;
|
||||
e->next = NULL;
|
||||
if (tail)
|
||||
tail->next = e;
|
||||
else
|
||||
_cache_heads[b] = e;
|
||||
tail = e;
|
||||
_cache_counts[b]++;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PORT
|
||||
void use_impulse_cache(int use)
|
||||
{
|
||||
EnterCriticalSection(&_cs_use_cache);
|
||||
_use_cache = use;
|
||||
LeaveCriticalSection(&_cs_use_cache);
|
||||
}
|
||||
|
||||
PORT
|
||||
void init_impulse_cache(int use)
|
||||
{
|
||||
//InitializeCriticalSection(&_cs_use_cache);
|
||||
InitializeCriticalSectionAndSpinCount(&_cs_use_cache, 2500);
|
||||
|
||||
EnterCriticalSection(&_cs_use_cache);
|
||||
_use_cache = use;
|
||||
LeaveCriticalSection(&_cs_use_cache);
|
||||
|
||||
_run = 1;
|
||||
}
|
||||
|
||||
PORT
|
||||
void destroy_impulse_cache(void)
|
||||
{
|
||||
_run = 0;
|
||||
|
||||
DeleteCriticalSection(&_cs_use_cache);
|
||||
|
||||
free_impulse_cache();
|
||||
}
|
||||
84
impulse_cache.h
Normal file
84
impulse_cache.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* impulse_cache.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2019, 2024 Warren Pratt, NR0V
|
||||
Copyright (C) 2025 Richard Samphire, MW0LGE
|
||||
|
||||
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
|
||||
mw0lge@grange-lane.co.uk
|
||||
|
||||
*/
|
||||
//
|
||||
//============================================================================================//
|
||||
// Dual-Licensing Statement (Applies Only to Author's Contributions, Richard Samphire MW0LGE) //
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// For any code originally written by Richard Samphire MW0LGE, or for any modifications //
|
||||
// made by him, the copyright holder for those portions (Richard Samphire) reserves the //
|
||||
// right to use, license, and distribute such code under different terms, including //
|
||||
// closed-source and proprietary licences, in addition to the GNU General Public License //
|
||||
// granted above. Nothing in this statement restricts any rights granted to recipients under //
|
||||
// the GNU GPL. Code contributed by others (not Richard Samphire) remains licensed under //
|
||||
// its original terms and is not affected by this dual-licensing statement in any way. //
|
||||
// Richard Samphire can be reached by email at : mw0lge@grange-lane.co.uk //
|
||||
//============================================================================================//
|
||||
|
||||
#ifndef _impulse_cache_h
|
||||
#define _impulse_cache_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_WIN64)
|
||||
// 64-bit build
|
||||
extern uint64_t fnv1a_hash64(const void* data, size_t len);
|
||||
#define GOLDEN_RATIO_64 0x9E3779B97F4A7C15ULL
|
||||
|
||||
#define HASH_T uint64_t
|
||||
#define fnv1a_hash fnv1a_hash64
|
||||
#define GOLDEN_RATIO GOLDEN_RATIO_64
|
||||
#else
|
||||
// 32-bit build
|
||||
extern uint32_t fnv1a_hash32(const void* data, size_t len);
|
||||
#define GOLDEN_RATIO_32 0x9e3779b9U
|
||||
|
||||
#define HASH_T uint32_t
|
||||
#define fnv1a_hash fnv1a_hash32
|
||||
#define GOLDEN_RATIO GOLDEN_RATIO_32
|
||||
#endif
|
||||
|
||||
#define MAX_CACHE_ENTRIES 4096 // max number of cache entires per cache bucket
|
||||
#define CACHE_BUCKETS 4 // 4 cache buckets, for fir_bandpass, mp, eq, fc. Unique indexes in the #defines below
|
||||
|
||||
#define FIR_CACHE 0
|
||||
#define MP_CACHE 1
|
||||
#define EQ_CACHE 2
|
||||
#define FC_CACHE 3
|
||||
|
||||
double* get_impulse_cache_entry(size_t bucket, HASH_T hash, int N);
|
||||
void add_impulse_to_cache(size_t bucket, HASH_T hash, int N, double* impulse);
|
||||
|
||||
__declspec (dllexport) int save_impulse_cache(const char* path);
|
||||
__declspec (dllexport) int read_impulse_cache(const char* path);
|
||||
__declspec (dllexport) void use_impulse_cache(int use);
|
||||
|
||||
__declspec (dllexport) void init_impulse_cache(int use);
|
||||
__declspec (dllexport) void destroy_impulse_cache(void);
|
||||
|
||||
#endif
|
||||
604
iobuffs.c
Normal file
604
iobuffs.c
Normal file
@ -0,0 +1,604 @@
|
||||
/* iobuffs.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Begin Slew Code *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
enum _slew
|
||||
{
|
||||
BEGIN = 0,
|
||||
DELAYUP,
|
||||
UPSLEW,
|
||||
ON,
|
||||
DELAYDOWN,
|
||||
DOWNSLEW,
|
||||
ZERO,
|
||||
OFF
|
||||
};
|
||||
|
||||
void create_slews (IOB a)
|
||||
{
|
||||
int i;
|
||||
double delta, theta;
|
||||
a->slew.ustate = BEGIN;
|
||||
a->slew.dstate = BEGIN;
|
||||
a->slew.ucount = 0;
|
||||
a->slew.dcount = 0;
|
||||
a->slew.ndelup = (int)(ch[a->channel].tdelayup * ch[a->channel].in_rate);
|
||||
a->slew.ndeldown = (int)(ch[a->channel].tdelaydown * ch[a->channel].out_rate);
|
||||
a->slew.ntup = (int)(ch[a->channel].tslewup * ch[a->channel].in_rate);
|
||||
a->slew.ntdown = (int)(ch[a->channel].tslewdown * ch[a->channel].out_rate);
|
||||
a->slew.cup = (double *) malloc0 ((a->slew.ntup + 1) * sizeof (double));
|
||||
a->slew.cdown = (double *) malloc0 ((a->slew.ntdown + 1) * sizeof (double));
|
||||
|
||||
delta = PI / (double)a->slew.ntup;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->slew.ntup; i++)
|
||||
{
|
||||
a->slew.cup[i] = 0.5 * (1.0 - cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
|
||||
delta = PI / (double)a->slew.ntdown;
|
||||
theta = 0.0;
|
||||
for (i = 0; i <= a->slew.ntdown; i++)
|
||||
{
|
||||
a->slew.cdown[i] = 0.5 * (1 + cos (theta));
|
||||
theta += delta;
|
||||
}
|
||||
|
||||
InterlockedBitTestAndReset (&a->slew.upflag, 0);
|
||||
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
||||
}
|
||||
|
||||
void destroy_slews(IOB a)
|
||||
{
|
||||
_aligned_free (a->slew.cdown);
|
||||
_aligned_free (a->slew.cup);
|
||||
}
|
||||
|
||||
void flush_slews (IOB a)
|
||||
{
|
||||
a->slew.ustate = BEGIN;
|
||||
a->slew.dstate = BEGIN;
|
||||
a->slew.ucount = 0;
|
||||
a->slew.dcount = 0;
|
||||
InterlockedBitTestAndReset (&a->slew.upflag, 0);
|
||||
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
||||
}
|
||||
|
||||
void upslew0 (IOB a, double* pin)
|
||||
{
|
||||
int i;
|
||||
double *pout;
|
||||
double I, Q;
|
||||
pout = a->r1_baseptr + 2 * a->r1_inidx;
|
||||
for (i = 0; i < a->in_size; i++)
|
||||
{
|
||||
I = pin[2 * i + 0];
|
||||
Q = pin[2 * i + 1];
|
||||
switch (a->slew.ustate)
|
||||
{
|
||||
case BEGIN:
|
||||
pout[2 * i + 0] = 0.0;
|
||||
pout[2 * i + 1] = 0.0;
|
||||
if ((I != 0.0) || (Q != 0.0))
|
||||
{
|
||||
if (a->slew.ndelup > 0)
|
||||
{
|
||||
a->slew.ustate = DELAYUP;
|
||||
a->slew.ucount = a->slew.ndelup;
|
||||
}
|
||||
else if (a->slew.ntup > 0)
|
||||
{
|
||||
a->slew.ustate = UPSLEW;
|
||||
a->slew.ucount = a->slew.ntup;
|
||||
}
|
||||
else
|
||||
a->slew.ustate = ON;
|
||||
}
|
||||
break;
|
||||
case DELAYUP:
|
||||
pout[2 * i + 0] = 0.0;
|
||||
pout[2 * i + 1] = 0.0;
|
||||
if (a->slew.ucount-- == 0)
|
||||
{
|
||||
if (a->slew.ntup > 0)
|
||||
{
|
||||
a->slew.ustate = UPSLEW;
|
||||
a->slew.ucount = a->slew.ntup;
|
||||
}
|
||||
else
|
||||
a->slew.ustate = ON;
|
||||
}
|
||||
break;
|
||||
case UPSLEW:
|
||||
pout[2 * i + 0] = I * a->slew.cup[a->slew.ntup - a->slew.ucount];
|
||||
pout[2 * i + 1] = Q * a->slew.cup[a->slew.ntup - a->slew.ucount];
|
||||
if (a->slew.ucount-- == 0)
|
||||
a->slew.ustate = ON;
|
||||
break;
|
||||
case ON:
|
||||
pout[2 * i + 0] = I;
|
||||
pout[2 * i + 1] = Q;
|
||||
if (i == a->in_size - 1)
|
||||
{
|
||||
a->slew.ustate = BEGIN;
|
||||
InterlockedBitTestAndReset (&a->slew.upflag, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void upslew2 (IOB a, INREAL* pIin, INREAL* pQin)
|
||||
{
|
||||
int i;
|
||||
double *pout;
|
||||
double I, Q;
|
||||
pout = a->r1_baseptr + 2 * a->r1_inidx;
|
||||
for (i = 0; i < a->in_size; i++)
|
||||
{
|
||||
I = (double)pIin[i];
|
||||
Q = (double)pQin[i];
|
||||
switch (a->slew.ustate)
|
||||
{
|
||||
case BEGIN:
|
||||
pout[2 * i + 0] = 0.0;
|
||||
pout[2 * i + 1] = 0.0;
|
||||
if ((I != 0.0) || (Q != 0.0))
|
||||
{
|
||||
if (a->slew.ndelup > 0)
|
||||
{
|
||||
a->slew.ustate = DELAYUP;
|
||||
a->slew.ucount = a->slew.ndelup;
|
||||
}
|
||||
else if (a->slew.ntup > 0)
|
||||
{
|
||||
a->slew.ustate = UPSLEW;
|
||||
a->slew.ucount = a->slew.ntup;
|
||||
}
|
||||
else
|
||||
a->slew.ustate = ON;
|
||||
}
|
||||
break;
|
||||
case DELAYUP:
|
||||
pout[2 * i + 0] = 0.0;
|
||||
pout[2 * i + 1] = 0.0;
|
||||
if (a->slew.ucount-- == 0)
|
||||
{
|
||||
if (a->slew.ntup > 0)
|
||||
{
|
||||
a->slew.ustate = UPSLEW;
|
||||
a->slew.ucount = a->slew.ntup;
|
||||
}
|
||||
else
|
||||
a->slew.ustate = ON;
|
||||
}
|
||||
break;
|
||||
case UPSLEW:
|
||||
pout[2 * i + 0] = I * a->slew.cup[a->slew.ntup - a->slew.ucount];
|
||||
pout[2 * i + 1] = Q * a->slew.cup[a->slew.ntup - a->slew.ucount];
|
||||
if (a->slew.ucount-- == 0)
|
||||
a->slew.ustate = ON;
|
||||
break;
|
||||
case ON:
|
||||
pout[2 * i + 0] = I;
|
||||
pout[2 * i + 1] = Q;
|
||||
if (i == a->in_size - 1)
|
||||
{
|
||||
a->slew.ustate = BEGIN;
|
||||
InterlockedBitTestAndReset (&a->slew.upflag, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void downslew0 (IOB a, double* pout)
|
||||
{
|
||||
int i;
|
||||
double *pin;
|
||||
double I, Q;
|
||||
pin = a->r2_baseptr + 2 * a->r2_outidx;
|
||||
for (i = 0; i < a->out_size; i++)
|
||||
{
|
||||
I = pin[2 * i + 0];
|
||||
Q = pin[2 * i + 1];
|
||||
switch (a->slew.dstate)
|
||||
{
|
||||
case BEGIN:
|
||||
pout[2 * i + 0] = I;
|
||||
pout[2 * i + 1] = Q;
|
||||
if (a->slew.ndeldown > 0)
|
||||
{
|
||||
a->slew.dstate = DELAYDOWN;
|
||||
a->slew.dcount = a->slew.ndeldown;
|
||||
}
|
||||
else if (a->slew.ntdown > 0)
|
||||
{
|
||||
a->slew.dstate = DOWNSLEW;
|
||||
a->slew.dcount = a->slew.ntdown;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->slew.dstate = ZERO;
|
||||
a->slew.dcount = a->out_size;
|
||||
}
|
||||
break;
|
||||
case DELAYDOWN:
|
||||
pout[2 * i + 0] = I;
|
||||
pout[2 * i + 1] = Q;
|
||||
if (a->slew.dcount-- == 0)
|
||||
{
|
||||
if (a->slew.ntdown > 0)
|
||||
{
|
||||
a->slew.dstate = DOWNSLEW;
|
||||
a->slew.dcount = a->slew.ntdown;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->slew.dstate = ZERO;
|
||||
a->slew.dcount = a->out_size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DOWNSLEW:
|
||||
pout[2 * i + 0] = I * a->slew.cdown[a->slew.ntdown - a->slew.dcount];
|
||||
pout[2 * i + 1] = Q * a->slew.cdown[a->slew.ntdown - a->slew.dcount];
|
||||
if (a->slew.dcount-- == 0)
|
||||
{
|
||||
a->slew.dstate = ZERO;
|
||||
a->slew.dcount = a->out_size;
|
||||
}
|
||||
break;
|
||||
case ZERO:
|
||||
pout[2 * i + 0] = 0.0;
|
||||
pout[2 * i + 1] = 0.0;
|
||||
if (a->slew.dcount-- == 0)
|
||||
a->slew.dstate = OFF;
|
||||
break;
|
||||
case OFF:
|
||||
pout[2 * i + 0] = 0.0;
|
||||
pout[2 * i + 1] = 0.0;
|
||||
if (i == a->out_size - 1)
|
||||
{
|
||||
a->slew.dstate = BEGIN;
|
||||
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void downslew2 (IOB a, OUTREAL* pIout, OUTREAL* pQout)
|
||||
{
|
||||
int i;
|
||||
double *pin;
|
||||
double I, Q;
|
||||
pin = a->r2_baseptr + 2 * a->r2_outidx;
|
||||
for (i = 0; i < a->out_size; i++)
|
||||
{
|
||||
I = pin[2 * i + 0];
|
||||
Q = pin[2 * i + 1];
|
||||
switch (a->slew.dstate)
|
||||
{
|
||||
case BEGIN:
|
||||
pIout[i] = (OUTREAL)I;
|
||||
pQout[i] = (OUTREAL)Q;
|
||||
if (a->slew.ndeldown > 0)
|
||||
{
|
||||
a->slew.dstate = DELAYDOWN;
|
||||
a->slew.dcount = a->slew.ndeldown;
|
||||
}
|
||||
else if (a->slew.ntdown > 0)
|
||||
{
|
||||
a->slew.dstate = DOWNSLEW;
|
||||
a->slew.dcount = a->slew.ntdown;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->slew.dstate = ZERO;
|
||||
a->slew.dcount = a->out_size;
|
||||
}
|
||||
break;
|
||||
case DELAYDOWN:
|
||||
pIout[i] = (OUTREAL)I;
|
||||
pQout[i] = (OUTREAL)Q;
|
||||
if (a->slew.dcount-- == 0)
|
||||
{
|
||||
if (a->slew.ntdown > 0)
|
||||
{
|
||||
a->slew.dstate = DOWNSLEW;
|
||||
a->slew.dcount = a->slew.ntdown;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->slew.dstate = ZERO;
|
||||
a->slew.dcount = a->out_size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DOWNSLEW:
|
||||
pIout[i] = (OUTREAL)(I * a->slew.cdown[a->slew.ntdown - a->slew.dcount]);
|
||||
pQout[i] = (OUTREAL)(Q * a->slew.cdown[a->slew.ntdown - a->slew.dcount]);
|
||||
if (a->slew.dcount-- == 0)
|
||||
{
|
||||
a->slew.dstate = ZERO;
|
||||
a->slew.dcount = a->out_size;
|
||||
}
|
||||
break;
|
||||
case ZERO:
|
||||
pIout[i] = 0.0;
|
||||
pQout[i] = 0.0;
|
||||
if (a->slew.dcount-- == 0)
|
||||
a->slew.dstate = OFF;
|
||||
break;
|
||||
case OFF:
|
||||
pIout[i] = 0.0;
|
||||
pQout[i] = 0.0;
|
||||
if (i == a->out_size - 1)
|
||||
{
|
||||
a->slew.dstate = BEGIN;
|
||||
InterlockedBitTestAndReset (&a->slew.downflag, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Begin Buffer Code *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
void create_iobuffs (int channel)
|
||||
{
|
||||
int n;
|
||||
IOB a = (IOB) malloc0 (sizeof(iob));
|
||||
ch[channel].iob.pc = ch[channel].iob.pd = ch[channel].iob.pe = ch[channel].iob.pf = a;
|
||||
a->channel = channel;
|
||||
a->in_size = ch[channel].in_size;
|
||||
a->r1_outsize = ch[channel].dsp_insize;
|
||||
if (a->r1_outsize > a->in_size)
|
||||
a->r1_size = a->r1_outsize;
|
||||
else
|
||||
a->r1_size = a->in_size;
|
||||
a->out_size = ch[channel].out_size;
|
||||
a->r2_insize = ch[channel].dsp_outsize;
|
||||
if (a->out_size > a->r2_insize)
|
||||
a->r2_size = a->out_size;
|
||||
else
|
||||
a->r2_size = a->r2_insize;
|
||||
a->r1_active_buffsize = DSP_MULT * a->r1_size;
|
||||
a->r2_active_buffsize = DSP_MULT * a->r2_size;
|
||||
a->r1_baseptr = (double*) malloc0 (a->r1_active_buffsize * sizeof (complex));
|
||||
a->r2_baseptr = (double*) malloc0 (a->r2_active_buffsize * sizeof (complex));
|
||||
a->r1_inidx = 0;
|
||||
a->r1_outidx = 0;
|
||||
a->r1_unqueuedsamps = 0;
|
||||
a->r2_inidx = (DSP_MULT - 1) * a->r2_size;
|
||||
a->r2_outidx = 0;
|
||||
a->r2_havesamps = (DSP_MULT - 1) * a->r2_size;
|
||||
n = a->r2_havesamps / a->out_size;
|
||||
a->r2_unqueuedsamps = a->r2_havesamps - n * a->out_size;
|
||||
InitializeCriticalSectionAndSpinCount(&a->r2_ControlSection, 2500);
|
||||
a->Sem_BuffReady = CreateSemaphore(0, 0, 1000, 0);
|
||||
a->Sem_OutReady = CreateSemaphore(0, n, 1000, 0);
|
||||
a->bfo = ch[channel].bfo;
|
||||
create_slews (a);
|
||||
|
||||
InterlockedBitTestAndReset(&a->flush_bypass, 0);
|
||||
a->Sem_Flush = CreateSemaphore(0, 0, 1, 0);
|
||||
_beginthread(flushChannel, 0, (void*)(uintptr_t)a->channel);
|
||||
}
|
||||
|
||||
void destroy_iobuffs (int channel)
|
||||
{
|
||||
IOB a = ch[channel].iob.pc;
|
||||
|
||||
InterlockedBitTestAndSet(&a->flush_bypass, 0);
|
||||
ReleaseSemaphore(a->Sem_Flush, 1, 0);
|
||||
while (InterlockedAnd(&a->flush_bypass, 0xffffffff)) Sleep(1);
|
||||
CloseHandle(a->Sem_Flush);
|
||||
|
||||
destroy_slews (a);
|
||||
CloseHandle (a->Sem_OutReady);
|
||||
CloseHandle (a->Sem_BuffReady);
|
||||
DeleteCriticalSection(&a->r2_ControlSection);
|
||||
_aligned_free (a->r2_baseptr);
|
||||
_aligned_free (a->r1_baseptr);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_iobuffs (int channel)
|
||||
{
|
||||
int n;
|
||||
IOB a = ch[channel].iob.pf;
|
||||
memset (a->r1_baseptr, 0, a->r1_active_buffsize * sizeof (complex));
|
||||
memset (a->r2_baseptr, 0, a->r2_active_buffsize * sizeof (complex));
|
||||
a->r1_inidx = 0;
|
||||
a->r1_outidx = 0;
|
||||
a->r1_unqueuedsamps = 0;
|
||||
a->r2_inidx = (DSP_MULT - 1) * a->r2_size;
|
||||
a->r2_outidx = 0;
|
||||
a->r2_havesamps = (DSP_MULT - 1) * a->r2_size;
|
||||
while (!WaitForSingleObject (a->Sem_BuffReady, 1));
|
||||
n = a->r2_havesamps / a->out_size;
|
||||
a->r2_unqueuedsamps = a->r2_havesamps - n * a->out_size;
|
||||
CloseHandle (a->Sem_OutReady);
|
||||
a->Sem_OutReady = CreateSemaphore(0, n, 1000, 0);
|
||||
flush_slews (a);
|
||||
}
|
||||
|
||||
|
||||
PORT //double, interleaved I/Q
|
||||
void fexchange0 (int channel, double* in, double* out, int* error)
|
||||
{
|
||||
int n;
|
||||
int doit = 0;
|
||||
IOB a;
|
||||
*error = 0;
|
||||
if (_InterlockedAnd (&ch[channel].exchange, 1))
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csEXCH);
|
||||
a = ch[channel].iob.pe;
|
||||
if (_InterlockedAnd (&a->slew.upflag, 1))
|
||||
upslew0 (a, in);
|
||||
else
|
||||
memcpy (a->r1_baseptr + 2 * a->r1_inidx, in, a->in_size * sizeof (complex));
|
||||
// add check with *error += -1; for case when r1 is full and an overwrite occurs
|
||||
if ((a->r1_unqueuedsamps += a->in_size) >= a->r1_outsize)
|
||||
{
|
||||
n = a->r1_unqueuedsamps / a->r1_outsize;
|
||||
ReleaseSemaphore(a->Sem_BuffReady, n, 0);
|
||||
a->r1_unqueuedsamps -= n * a->r1_outsize;
|
||||
}
|
||||
if ((a->r1_inidx += a->in_size) == a->r1_active_buffsize)
|
||||
a->r1_inidx = 0;
|
||||
|
||||
EnterCriticalSection (&a->r2_ControlSection);
|
||||
if (a->r2_havesamps >= a->out_size)
|
||||
doit = 1;
|
||||
if ((a->r2_havesamps -= a->out_size) < 0) a->r2_havesamps = 0;
|
||||
LeaveCriticalSection (&a->r2_ControlSection);
|
||||
if (a->bfo) WaitForSingleObject (a->Sem_OutReady, INFINITE);
|
||||
if (a->bfo || doit)
|
||||
if (_InterlockedAnd (&a->slew.downflag, 1))
|
||||
{
|
||||
downslew0 (a, out);
|
||||
if (!_InterlockedAnd (&a->slew.downflag, 1))
|
||||
{
|
||||
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||
ReleaseSemaphore(a->Sem_Flush, 1, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
memcpy (out, a->r2_baseptr + 2 * a->r2_outidx, a->out_size * sizeof (complex));
|
||||
else
|
||||
{
|
||||
memset (out, 0, a->out_size * sizeof (complex));
|
||||
*error += -2;
|
||||
}
|
||||
if ((a->r2_outidx += a->out_size) == a->r2_active_buffsize)
|
||||
a->r2_outidx = 0;
|
||||
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||
}
|
||||
}
|
||||
|
||||
PORT //separate I/Q buffers
|
||||
void fexchange2 (int channel, INREAL *Iin, INREAL *Qin, OUTREAL *Iout, OUTREAL *Qout, int* error)
|
||||
{
|
||||
int i, n;
|
||||
int doit = 0;
|
||||
IOB a;
|
||||
*error = 0;
|
||||
if (_InterlockedAnd (&ch[channel].exchange, 1))
|
||||
{
|
||||
EnterCriticalSection (&ch[channel].csEXCH);
|
||||
a = ch[channel].iob.pe;
|
||||
if (_InterlockedAnd (&a->slew.upflag, 1))
|
||||
upslew2 (a, Iin, Qin);
|
||||
else
|
||||
for (i = 0; i < a->in_size; i++)
|
||||
{
|
||||
(a->r1_baseptr + 2 * a->r1_inidx)[2 * i + 0] = (double)(Iin[i]);
|
||||
(a->r1_baseptr + 2 * a->r1_inidx)[2 * i + 1] = (double)(Qin[i]);
|
||||
}
|
||||
// add check with *error += -1; for case when r1 is full and an overwrite occurs
|
||||
if ((a->r1_unqueuedsamps += a->in_size) >= a->r1_outsize)
|
||||
{
|
||||
n = a->r1_unqueuedsamps / a->r1_outsize;
|
||||
ReleaseSemaphore(a->Sem_BuffReady, n, 0);
|
||||
a->r1_unqueuedsamps -= n * a->r1_outsize;
|
||||
}
|
||||
if ((a->r1_inidx += a->in_size) == a->r1_active_buffsize)
|
||||
a->r1_inidx = 0;
|
||||
|
||||
EnterCriticalSection (&a->r2_ControlSection);
|
||||
if (a->r2_havesamps >= a->out_size)
|
||||
doit = 1;
|
||||
if ((a->r2_havesamps -= a->out_size) < 0) a->r2_havesamps = 0;
|
||||
LeaveCriticalSection (&a->r2_ControlSection);
|
||||
if (a->bfo) WaitForSingleObject (a->Sem_OutReady, INFINITE);
|
||||
if (a->bfo || doit)
|
||||
{
|
||||
if (_InterlockedAnd (&a->slew.downflag, 1))
|
||||
{
|
||||
downslew2 (a, Iout, Qout);
|
||||
if (!_InterlockedAnd (&a->slew.downflag, 1))
|
||||
{
|
||||
InterlockedBitTestAndReset (&ch[channel].exchange, 0);
|
||||
ReleaseSemaphore(a->Sem_Flush, 1, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
for (i = 0; i < a->out_size; i++)
|
||||
{
|
||||
Iout[i] = (OUTREAL)((a->r2_baseptr + 2 * a->r2_outidx)[2 * i + 0]);
|
||||
Qout[i] = (OUTREAL)((a->r2_baseptr + 2 * a->r2_outidx)[2 * i + 1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memset (Iout, 0, a->out_size * sizeof (OUTREAL));
|
||||
memset (Qout, 0, a->out_size * sizeof (OUTREAL));
|
||||
*error += -2;
|
||||
}
|
||||
if ((a->r2_outidx += a->out_size) == a->r2_active_buffsize)
|
||||
a->r2_outidx = 0;
|
||||
LeaveCriticalSection (&ch[channel].csEXCH);
|
||||
}
|
||||
}
|
||||
|
||||
void dexchange (int channel, double* in, double* out)
|
||||
{
|
||||
int n;
|
||||
IOB a = ch[channel].iob.pd;
|
||||
if (!_InterlockedAnd (&ch[channel].run, 1)) _endthread();
|
||||
|
||||
EnterCriticalSection (&a->r2_ControlSection);
|
||||
a->r2_havesamps += a->r2_insize;
|
||||
LeaveCriticalSection (&a->r2_ControlSection);
|
||||
memcpy (a->r2_baseptr + 2 * a->r2_inidx, in, a->r2_insize * sizeof (complex));
|
||||
if ((a->r2_inidx += a->r2_insize) == a->r2_active_buffsize)
|
||||
a->r2_inidx = 0;
|
||||
if (a->bfo && (a->r2_unqueuedsamps += a->r2_insize) >= a->out_size)
|
||||
{
|
||||
n = a->r2_unqueuedsamps / a->out_size;
|
||||
ReleaseSemaphore(a->Sem_OutReady, n, 0);
|
||||
a->r2_unqueuedsamps -= n * a->out_size;
|
||||
}
|
||||
memcpy (out, a->r1_baseptr + 2 * a->r1_outidx, a->r1_outsize * sizeof (complex));
|
||||
if ((a->r1_outidx += a->r1_outsize) == a->r1_active_buffsize)
|
||||
a->r1_outidx = 0;
|
||||
}
|
||||
97
iobuffs.h
Normal file
97
iobuffs.h
Normal file
@ -0,0 +1,97 @@
|
||||
/* iobuffs.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _iobuffs_h
|
||||
#define _iobuffs_h
|
||||
#include "comm.h"
|
||||
typedef struct _iobf
|
||||
{
|
||||
int channel;
|
||||
int in_size; // input number of complex samples in a fexchange call
|
||||
int out_size; // output number of complex samples in a fexchange call
|
||||
int r1_outsize; // number of complex samples taken out of the input-pseudo-ring for processing
|
||||
int r2_insize; // number of processed complex samples returned into the output-pseudo-ring
|
||||
int r1_size; // size of a single maximum sized transfer
|
||||
int r2_size; // size of a single maximum sized transfer
|
||||
int r1_active_buffsize; // size of input pseudo-ring (in complex samples)
|
||||
int r2_active_buffsize; // size of output pseudo-ring (in complex samples)
|
||||
|
||||
double* r1_baseptr; // pointer to input pseudo-ring
|
||||
int r1_inidx; // in 'double', actual index into the buffer is 2 times this
|
||||
int r1_outidx; // in 'double', actual index into the buffer is 2 times this
|
||||
int r1_unqueuedsamps; // number of input samples not yet queued/released for execution
|
||||
|
||||
double* r2_baseptr; // pointer to output pseudo-ring
|
||||
int r2_inidx; // in 'double', actual index into the buffer is 2 times this
|
||||
int r2_outidx; // in 'double', actual index into the buffer is 2 times this
|
||||
int r2_havesamps; // number of processed samples in output pseudo-ring
|
||||
int r2_unqueuedsamps; // number of output samples not yet queued / released for output
|
||||
CRITICAL_SECTION r2_ControlSection;
|
||||
|
||||
int bfo; // block_for_output, wait until output is available before proceeding
|
||||
HANDLE Sem_OutReady; // count = number of 'out_size' buffers processed and available for output
|
||||
HANDLE Sem_BuffReady; // count = number of 'dsp_size' buffers queued for processing
|
||||
volatile long exec_bypass;
|
||||
volatile long flush_bypass;
|
||||
HANDLE Sem_Flush;
|
||||
struct
|
||||
{
|
||||
int ustate;
|
||||
int dstate;
|
||||
int ucount;
|
||||
int dcount;
|
||||
int ndelup;
|
||||
int ntup;
|
||||
double* cup;
|
||||
int ndeldown;
|
||||
int ntdown;
|
||||
double* cdown;
|
||||
volatile long upflag;
|
||||
volatile long downflag;
|
||||
} slew;
|
||||
} iob, *IOB;
|
||||
|
||||
extern void create_slews (IOB a);
|
||||
|
||||
extern void destroy_slews (IOB a);
|
||||
|
||||
extern void flush_slews (IOB a);
|
||||
|
||||
extern void create_iobuffs (int channel);
|
||||
|
||||
extern void destroy_iobuffs (int channel);
|
||||
|
||||
extern void flush_iobuffs (int channel);
|
||||
|
||||
PORT // double, interleaved I/Q
|
||||
void fexchange0 (int channel, double* in, double* out, int* error);
|
||||
|
||||
PORT // separate I/Q buffers
|
||||
extern void fexchange2 (int channel, INREAL *Iin, INREAL *Qin, OUTREAL *Iout, OUTREAL *Qout, int* error);
|
||||
|
||||
extern void dexchange (int channel, double* in, double* out);
|
||||
|
||||
#endif
|
||||
315
iqc.c
Normal file
315
iqc.c
Normal file
@ -0,0 +1,315 @@
|
||||
/* iqc.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013, 2016 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void size_iqc (IQC a)
|
||||
{
|
||||
int i;
|
||||
a->t = (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] = (double *) malloc0 (a->ints * 4 * sizeof(double));
|
||||
a->cc[i] = (double *) malloc0 (a->ints * 4 * sizeof(double));
|
||||
a->cs[i] = (double *) malloc0 (a->ints * 4 * sizeof(double));
|
||||
}
|
||||
a->dog.cpi = (int *) malloc0 (a->ints * sizeof (int));
|
||||
a->dog.count = 0;
|
||||
a->dog.full_ints = 0;
|
||||
}
|
||||
|
||||
void desize_iqc (IQC a)
|
||||
{
|
||||
int i;
|
||||
_aligned_free (a->dog.cpi);
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
_aligned_free (a->cm[i]);
|
||||
_aligned_free (a->cc[i]);
|
||||
_aligned_free (a->cs[i]);
|
||||
}
|
||||
_aligned_free (a->t);
|
||||
}
|
||||
|
||||
void 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 = (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;
|
||||
}
|
||||
InitializeCriticalSectionAndSpinCount (&a->dog.cs, 2500);
|
||||
size_iqc (a);
|
||||
}
|
||||
|
||||
void decalc_iqc (IQC a)
|
||||
{
|
||||
desize_iqc (a);
|
||||
DeleteCriticalSection (&a->dog.cs);
|
||||
_aligned_free (a->cup);
|
||||
}
|
||||
|
||||
IQC create_iqc (int run, int size, double* in, double* out, double rate, int ints, double tup, int spi)
|
||||
{
|
||||
IQC a = (IQC) malloc0 (sizeof (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 destroy_iqc (IQC a)
|
||||
{
|
||||
decalc_iqc (a);
|
||||
_aligned_free (a);
|
||||
}
|
||||
|
||||
void flush_iqc (IQC a)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
enum _iqcstate
|
||||
{
|
||||
RUN = 0,
|
||||
BEGIN,
|
||||
SWAP,
|
||||
END,
|
||||
DONE
|
||||
};
|
||||
|
||||
void xiqc (IQC a)
|
||||
{
|
||||
if (_InterlockedAnd(&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)
|
||||
{
|
||||
EnterCriticalSection (&a->dog.cs);
|
||||
++a->dog.count;
|
||||
LeaveCriticalSection (&a->dog.cs);
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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 (complex));
|
||||
}
|
||||
|
||||
void setBuffers_iqc (IQC a, double* in, double* out)
|
||||
{
|
||||
a->in = in;
|
||||
a->out = out;
|
||||
}
|
||||
|
||||
void setSamplerate_iqc (IQC a, int rate)
|
||||
{
|
||||
decalc_iqc (a);
|
||||
a->rate = rate;
|
||||
calc_iqc (a);
|
||||
}
|
||||
|
||||
void setSize_iqc (IQC a, int size)
|
||||
{
|
||||
a->size = size;
|
||||
}
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* TXA Properties *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
PORT
|
||||
void GetTXAiqcValues (int channel, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].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));
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAiqcValues (int channel, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC a;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
a = txa[channel].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;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAiqcSwap (int channel, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC a = txa[channel].iqc.p1;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
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));
|
||||
InterlockedBitTestAndSet (&a->busy, 0);
|
||||
a->state = SWAP;
|
||||
a->count = 0;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
while (_InterlockedAnd (&a->busy, 1)) Sleep(1);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAiqcStart (int channel, double* cm, double* cc, double* cs)
|
||||
{
|
||||
IQC a = txa[channel].iqc.p1;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
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));
|
||||
InterlockedBitTestAndSet (&a->busy, 0);
|
||||
a->state = BEGIN;
|
||||
a->count = 0;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
InterlockedBitTestAndSet (&txa[channel].iqc.p1->run, 0);
|
||||
while (_InterlockedAnd (&a->busy, 1)) Sleep(1);
|
||||
}
|
||||
|
||||
PORT
|
||||
void SetTXAiqcEnd (int channel)
|
||||
{
|
||||
IQC a = txa[channel].iqc.p1;
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
InterlockedBitTestAndSet (&a->busy, 0);
|
||||
a->state = END;
|
||||
a->count = 0;
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
while (_InterlockedAnd (&a->busy, 1)) Sleep(1);
|
||||
InterlockedBitTestAndReset (&txa[channel].iqc.p1->run, 0);
|
||||
}
|
||||
|
||||
void GetTXAiqcDogCount (int channel, int* count)
|
||||
{
|
||||
IQC a = txa[channel].iqc.p1;
|
||||
EnterCriticalSection (&a->dog.cs);
|
||||
*count = a->dog.count;
|
||||
LeaveCriticalSection (&a->dog.cs);
|
||||
}
|
||||
|
||||
void SetTXAiqcDogCount (int channel, int count)
|
||||
{
|
||||
IQC a = txa[channel].iqc.p1;
|
||||
EnterCriticalSection (&a->dog.cs);
|
||||
a->dog.count = count;
|
||||
LeaveCriticalSection (&a->dog.cs);
|
||||
}
|
||||
93
iqc.h
Normal file
93
iqc.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* iqc.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _iqc_h
|
||||
#define _iqc_h
|
||||
|
||||
typedef struct _iqc
|
||||
{
|
||||
volatile long run;
|
||||
volatile 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;
|
||||
CRITICAL_SECTION cs;
|
||||
} dog;
|
||||
} iqc, *IQC;
|
||||
|
||||
extern IQC create_iqc (int run, int size, double* in, double* out, double rate, int ints, double tup, int spi);
|
||||
|
||||
extern void destroy_iqc (IQC a);
|
||||
|
||||
extern void flush_iqc (IQC a);
|
||||
|
||||
extern void xiqc (IQC a);
|
||||
|
||||
extern void setBuffers_iqc (IQC a, double* in, double* out);
|
||||
|
||||
extern void setSamplerate_iqc (IQC a, int rate);
|
||||
|
||||
extern void setSize_iqc (IQC a, int size);
|
||||
|
||||
extern void size_iqc (IQC a);
|
||||
|
||||
extern void desize_iqc (IQC a);
|
||||
|
||||
// TXA Properties
|
||||
|
||||
extern __declspec (dllexport) void GetTXAiqcValues (int channel, double* cm, double* cc, double* cs);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAiqcValues (int channel, double* cm, double* cc, double* cs);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAiqcSwap (int channel, double* cm, double* cc, double* cs);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAiqcStart (int channel, double* cm, double* cc, double* cs);
|
||||
|
||||
extern __declspec (dllexport) void SetTXAiqcEnd (int channel);
|
||||
|
||||
void GetTXAiqcDogCount (int channel, int* count);
|
||||
|
||||
void SetTXAiqcDogCount (int channel, int count);
|
||||
|
||||
#endif
|
||||
882
java/org/openhpsdr/dsp/Wdsp.java
Normal file
882
java/org/openhpsdr/dsp/Wdsp.java
Normal file
@ -0,0 +1,882 @@
|
||||
package org.openhpsdr.dsp;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Java native interface for using WDSP library (version 1.18).
|
||||
*
|
||||
* For details see WDSP_Guide.pdf.
|
||||
*
|
||||
* Note: Not for all the methods in WDSP a native call is made... some work ahead...
|
||||
*
|
||||
*/
|
||||
public class Wdsp {
|
||||
|
||||
private static Wdsp instance = null;
|
||||
|
||||
public static Wdsp getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Wdsp();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Wdsp() {
|
||||
// Use the getInstance method; not allowed to instantiate; singleton.
|
||||
}
|
||||
|
||||
static {
|
||||
|
||||
System.out.println("JVM is " + System.getProperty("os.arch") + " bit");
|
||||
String osName = System.getProperty("os.name");
|
||||
String vmName = System.getProperty("java.vm.name");
|
||||
boolean isAndroid = vmName != null && (vmName.contains("Dalvik") || vmName.contains("ART"));
|
||||
|
||||
if (isAndroid) {
|
||||
System.loadLibrary("wdsp");
|
||||
System.loadLibrary("wdspj");
|
||||
} else if(osName.startsWith("Windows")) {
|
||||
|
||||
String libraryPath=System.getProperty("user.dir")+File.separator+"lib"+File.separator+"windows";
|
||||
|
||||
if (System.getProperty("os.arch") != null && System.getProperty("os.arch").endsWith("64")) {
|
||||
libraryPath = libraryPath + File.separator + "fftw_x64";
|
||||
} else {
|
||||
libraryPath = libraryPath + File.separator + "fftw_x86";
|
||||
}
|
||||
System.load(libraryPath+File.separator+"libfftw3-3.dll");
|
||||
System.load(libraryPath+File.separator+"wdsp.dll");
|
||||
} else if(osName.startsWith("Linux")) {
|
||||
|
||||
String libraryPath=System.getProperty("user.dir")+File.separator+"lib"+File.separator+"linux";
|
||||
System.load(libraryPath+File.separator+"libfftw3.so");
|
||||
System.load(libraryPath+File.separator+"libwdsp.so");
|
||||
System.load(libraryPath+File.separator+"libwdspj.so");
|
||||
System.loadLibrary("wdspj");
|
||||
} else if(osName.startsWith("Mac")) {
|
||||
|
||||
String libraryPath=System.getProperty("user.dir")+File.separator+"lib"+File.separator+"mac";
|
||||
System.load(libraryPath+File.separator+"libfftw3.3.dylib");
|
||||
System.load(libraryPath+File.separator+"libwdsp.dylib");
|
||||
System.loadLibrary("wdsp");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WDSP version.
|
||||
*
|
||||
* @return version/
|
||||
*/
|
||||
public native int GetWDSPVersion();
|
||||
|
||||
/**
|
||||
* FFTW wisdom file
|
||||
*/
|
||||
public native int WDSPwisdom(String dir);
|
||||
|
||||
/**
|
||||
* Channel Settings / Information.
|
||||
*/
|
||||
public native void OpenChannel(
|
||||
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);
|
||||
|
||||
public native void CloseChannel(int channel);
|
||||
|
||||
public native int SetChannelState(int channel, int state, int dmode);
|
||||
|
||||
public native void fexchange0(
|
||||
int channel,
|
||||
double[] in,
|
||||
double[] out,
|
||||
int[] error);
|
||||
|
||||
public native void fexchange2(int channel,
|
||||
float[] Iin,
|
||||
float[] Qin,
|
||||
float[] Iout,
|
||||
float[] Qout,
|
||||
int[] error);
|
||||
|
||||
/**
|
||||
* RXA Receiver Unit.
|
||||
*/
|
||||
|
||||
public native void SetRXAAGCMaxInputLevel (int channel, double level);
|
||||
|
||||
/**
|
||||
* Frequency Shifter
|
||||
*/
|
||||
|
||||
public native void SetRXAShiftRun (int channel, int run);
|
||||
|
||||
public native void SetRXAShiftFreq (int channel, double fshift);
|
||||
|
||||
/**
|
||||
* Signal Generator
|
||||
*/
|
||||
|
||||
public native void SetRXAPreGenRun (int channel, int run);
|
||||
|
||||
public native void SetRXAPreGenMode (int channel, int mode);
|
||||
|
||||
public native void SetRXAPreGenToneMag (int channel, double mag);
|
||||
|
||||
public native void SetRXAPreGenToneFreq (int channel, double freq);
|
||||
|
||||
public native void SetRXAPreGenNoiseMag (int channel, double mag);
|
||||
|
||||
public native void SetRXAPreGenSweepMag (int channel, double mag);
|
||||
|
||||
public native void SetRXAPreGenSweepFreq (int channel, double freq1, double freq2);
|
||||
|
||||
public native void SetRXAPreGenSweepRate (int channel, double rate);
|
||||
|
||||
/**
|
||||
* Input, S, and AGC Meters
|
||||
*/
|
||||
public native double GetRXAMeter (int channel, int mt);
|
||||
|
||||
/**
|
||||
* Bandpass Filter Settings
|
||||
*/
|
||||
public native void RXANBPSetRun (int channel, int run);
|
||||
|
||||
public native void RXANBPSetWindow (int channel, int wintype);
|
||||
|
||||
/**
|
||||
* Notch Filter Settings
|
||||
*/
|
||||
public native void RXANBPSetNotchesRun (int channel, int run);
|
||||
|
||||
public native void RXANBPGetMinNotchWidth (int channel, double[] minwidth);
|
||||
|
||||
public native void RXANBPSetAutoIncrease (int channel, int autoincr);
|
||||
|
||||
public native void RXANBPSetTuneFrequency (int channel, double tunefreq);
|
||||
|
||||
public native void RXANBPSetShiftFrequency (int channel, double shift);
|
||||
|
||||
public native void RXANBPGetNumNotches (int channel, int[] nnotches);
|
||||
|
||||
public native int RXANBPAddNotch (int channel,
|
||||
int notch,
|
||||
double fcenter,
|
||||
double fwidth,
|
||||
int active);
|
||||
|
||||
public native int RXANBPGetNotch (int channel,
|
||||
int notch,
|
||||
double[] fcenter,
|
||||
double[] fwidth,
|
||||
int[] active);
|
||||
|
||||
public native int RXANBPDeleteNotch (int channel, int notch);
|
||||
|
||||
public native int RXANBPEditNotch (int channel,
|
||||
int notch,
|
||||
double fcenter,
|
||||
double fwidth,
|
||||
int active);
|
||||
|
||||
/**
|
||||
* Post-filter Display Sender
|
||||
*/
|
||||
|
||||
public native void SetRXASpectrum (int channel,
|
||||
int flag,
|
||||
int disp,
|
||||
int ss,
|
||||
int LO);
|
||||
|
||||
/**
|
||||
* AM Squelch
|
||||
*/
|
||||
|
||||
public native void SetRXAAMSQRun (int channel, int run);
|
||||
|
||||
public native void SetRXAAMSQThreshold (int channel, double threshold);
|
||||
|
||||
public native void SetRXAAMSQMaxTail (int channel, double tail);
|
||||
|
||||
/**
|
||||
* AM/SAM Demodulator
|
||||
*/
|
||||
|
||||
public native void SetRXAAMDSBMode (int channel, int sbmode);
|
||||
|
||||
public native void SetRXAAMDFadeLevel (int channel, int levelfade);
|
||||
|
||||
/**
|
||||
* FM Demodulator
|
||||
*/
|
||||
|
||||
public native void SetRXAFMDeviation (int channel, double deviation);
|
||||
|
||||
public native void SetRXACTCSSRun (int channel, int run);
|
||||
|
||||
public native void SetRXACTCSSFreq (int channel, double freq);
|
||||
|
||||
/**
|
||||
* FM Squelch
|
||||
*/
|
||||
|
||||
public native void SetRXAFMSQRun (int channel, int run);
|
||||
|
||||
public native void SetRXAFMSQThreshold (int channel, double threshold);
|
||||
|
||||
/**
|
||||
* Spectral Noise Blanker
|
||||
*/
|
||||
|
||||
public native void SetRXASNBARun (int channel, int run);
|
||||
|
||||
/**
|
||||
* NR3 / NR4
|
||||
*/
|
||||
|
||||
public native void SetRXARNNRRun (int channel, int run);
|
||||
|
||||
public native void SetRXARNNRPosition (int channel, int position);
|
||||
|
||||
public native void SetRXASBNRRun (int channel, int run);
|
||||
|
||||
public native void SetRXASBNRreductionAmount (int channel, float amount);
|
||||
|
||||
public native void SetRXASBNRsmoothingFactor (int channel, float factor);
|
||||
|
||||
public native void SetRXASBNRwhiteningFactor (int channel, float factor);
|
||||
|
||||
public native void SetRXASBNRnoiseRescale (int channel, float factor);
|
||||
|
||||
public native void SetRXASBNRpostFilterThreshold (int channel, float threshold);
|
||||
|
||||
public native void SetRXASBNRnoiseScalingType (int channel, int noiseScalingType);
|
||||
|
||||
public native void SetRXASBNRPosition (int channel, int position);
|
||||
|
||||
/**
|
||||
* Equalizer
|
||||
*/
|
||||
|
||||
public native void SetRXAEQRun (int channel, int run);
|
||||
|
||||
public native void SetRXAEQWintype (int channel, int wintype);
|
||||
|
||||
public native void SetRXAEQCtfmode (int channel, int mode);
|
||||
|
||||
public native void SetRXAEQProfile (int channel, int nfreqs, double[] F, double[] G);
|
||||
|
||||
/**
|
||||
* AGC
|
||||
*/
|
||||
|
||||
public native void SetRXAAGCMode (int channel, int mode);
|
||||
|
||||
public native void SetRXAAGCAttack (int channel, int attack);
|
||||
|
||||
public native void SetRXAAGCDecay (int channel, int decay);
|
||||
|
||||
public native void SetRXAAGCHang(int channel, int hang);
|
||||
|
||||
public native void GetRXAAGCHangLevel (int channel, double[] hanglevel);
|
||||
|
||||
public native void SetRXAAGCHangLevel (int channel, double hanglevel);
|
||||
|
||||
public native void GetRXAAGCHangThreshold (int channel, int[] hangthreshold);
|
||||
|
||||
public native void SetRXAAGCHangThreshold (int channel, int hangthreshold);
|
||||
|
||||
public native void GetRXAAGCThresh (int channel, double[] thresh, double size, double rate);
|
||||
|
||||
public native void SetRXAAGCThresh (int channel, double thresh, double size, double rate);
|
||||
|
||||
public native void GetRXAAGCTop (int channel, double[] max_agc);
|
||||
|
||||
public native void SetRXAAGCTop(int channel, double max_agc);
|
||||
|
||||
public native void SetRXAAGCSlope (int channel, int slope);
|
||||
|
||||
public native void SetRXAAGCFixed (int channel, double fixed_agc);
|
||||
|
||||
/**
|
||||
* Automatic Notch Filter
|
||||
*/
|
||||
|
||||
public native void SetRXAANFRun (int channel, int run);
|
||||
|
||||
public native void SetRXAANFTaps (int channel, int taps);
|
||||
|
||||
public native void SetRXAANFDelay (int channel, int delay);
|
||||
|
||||
public native void SetRXAANFGain (int channel, double gain);
|
||||
|
||||
public native void SetRXAANFLeakage (int channel, double leakage);
|
||||
|
||||
public native void SetRXAANFVals (int channel, int taps, int delay, double gain, double leakage);
|
||||
|
||||
public native void SetRXAANFPosition (int channel, int position);
|
||||
|
||||
|
||||
/**
|
||||
* LMS Noise Reduction
|
||||
*/
|
||||
|
||||
public native void SetRXAANRRun (int channel, int run);
|
||||
|
||||
public native void SetRXAANRTaps (int channel, int taps);
|
||||
|
||||
public native void SetRXAANRDelay (int channel, int delay);
|
||||
|
||||
public native void SetRXAANRGain (int channel, double gain);
|
||||
|
||||
public native void SetRXAANRLeakage (int channel, double leakage);
|
||||
|
||||
public native void SetRXAANRVals (int channel, int taps, int delay, double gain, double leakage);
|
||||
|
||||
/**
|
||||
* Spectral Noise Reduction
|
||||
*/
|
||||
|
||||
public native void SetRXAEMNRRun (int channel, int run);
|
||||
|
||||
public native void SetRXAEMNRgainMethod(int channel, int method);
|
||||
|
||||
public native void SetRXAEMNRnpeMethod(int channel, int method);
|
||||
|
||||
public native void SetRXAEMNRaeRun(int channel, int run);
|
||||
|
||||
public native void SetRXAEMNRPosition(int channel, int position);
|
||||
|
||||
/**
|
||||
* Bandpass Filter
|
||||
*/
|
||||
|
||||
public native void SetRXABandpassWindow (int channel, int wintype);
|
||||
|
||||
/**
|
||||
* Scope/Phase Display Sender
|
||||
*/
|
||||
|
||||
public native void RXAGetaSipF (int channel, float[] out, int size);
|
||||
|
||||
public native void RXAGetaSipF1 (int channel, float[] out, int size);
|
||||
|
||||
/**
|
||||
* AM Carrier Block
|
||||
*/
|
||||
|
||||
public native void SetRXACBLRun (int channel, int run);
|
||||
|
||||
/**
|
||||
* CW Peaking Filter
|
||||
*/
|
||||
|
||||
public native void SetRXASPCWRun (int channel, int run);
|
||||
|
||||
public native void SetRXASPCWFreq (int channel, double freq);
|
||||
|
||||
public native void SetRXASPCWBandwidth (int channel, double bw);
|
||||
|
||||
public native void SetRXASPCWGain (int channel, double gain);
|
||||
|
||||
/**
|
||||
* Dolly Filter
|
||||
*/
|
||||
|
||||
public native void SetRXAmpeakRun (int channel, int run);
|
||||
|
||||
public native void SetRXAmpeakNpeaks (int channel, int npeaks);
|
||||
|
||||
public native void SetRXAmpeakFilEnable (int channel, int fil, int enable);
|
||||
|
||||
public native void SetRXAmpeakFilFreq (int channel, int fil, double freq);
|
||||
|
||||
public native void SetRXAmpeakFilBw (int channel, int fil, double bw);
|
||||
|
||||
public native void SetRXAmpeakFilGain (int channel, int fil, double gain);
|
||||
|
||||
/**
|
||||
* Patch Panel - Audio Output Configuration
|
||||
*/
|
||||
|
||||
public native void SetRXAPanelRun (int channel, int run);
|
||||
|
||||
public native void SetRXAPanelSelect (int channel, int select);
|
||||
|
||||
public native void SetRXAPanelGain1 (int channel, double gain);
|
||||
|
||||
public native void SetRXAPanelGain2 (int channel, double gainI, double gainQ);
|
||||
|
||||
public native void SetRXAPanelPan (int channel, double pan);
|
||||
|
||||
public native void SetRXAPanelCopy (int channel, int copy);
|
||||
|
||||
public native void SetRXAPanelBinaural (int channel, int bin);
|
||||
|
||||
|
||||
/**
|
||||
* RXA Collectives & General Controls
|
||||
*/
|
||||
|
||||
public static final int LSB = 0;
|
||||
public static final int USB = 1;
|
||||
public static final int DSB = 2;
|
||||
public static final int CWL = 3;
|
||||
public static final int CWU = 4;
|
||||
public static final int FM = 5;
|
||||
public static final int AM = 6;
|
||||
public static final int DIGU = 7;
|
||||
public static final int SPEC = 8;
|
||||
public static final int DIGL = 9;
|
||||
public static final int SAM = 10;
|
||||
public static final int DRM = 11;
|
||||
|
||||
public native void SetRXAMode(int channel, int mode);
|
||||
|
||||
public native void RXASetPassband (int channel, double f_low, double f_high);
|
||||
|
||||
public native void RXASetNC (int channel, int nc);
|
||||
|
||||
public native void RXASetMP (int channel, int mp);
|
||||
|
||||
/**
|
||||
* The TXA Transmitter Unit
|
||||
*/
|
||||
|
||||
public native void SetTXAPreGenRun (int channel, int run);
|
||||
|
||||
public native void SetTXAPreGenMode (int channel, int mode);
|
||||
|
||||
public native void SetTXAPreGenToneMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPreGenToneFreq (int channel, double freq);
|
||||
|
||||
public native void SetTXAPreGenNoiseMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPreGenSweepMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPreGenSweepFreq (int channel, double freq1, double freq2);
|
||||
|
||||
public native void SetTXAPreGenSweepRate (int channel, double rate);
|
||||
|
||||
public native void SetTXAPreGenSawtoothMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPreGenSawtoothFreq (int channel, double freq);
|
||||
|
||||
public native void SetTXAPreGenTriangleMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPreGenTriangleFreq (int channel, double freq);
|
||||
|
||||
public native void SetTXAPreGenPulseMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPreGenPulseFreq (int channel, double freq);
|
||||
|
||||
public native void SetTXAPreGenPulseDutyCycle (int channel, double dc);
|
||||
|
||||
public native void SetTXAPreGenPulseToneFreq (int channel, double freq);
|
||||
|
||||
public native void SetTXAPreGenPulseTransition (int channel, double transtime);
|
||||
|
||||
public native void SetTXAPostGenRun (int channel, int run);
|
||||
|
||||
public native void SetTXAPostGenMode (int channel, int mode);
|
||||
|
||||
public native void SetTXAPostGenToneMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPostGenToneFreq (int channel, double freq);
|
||||
|
||||
public native void SetTXAPostGenTTMag (int channel, double mag1, double mag2);
|
||||
|
||||
public native void SetTXAPostGenTTFreq (int channel, double freq1, double freq2);
|
||||
|
||||
public native void SetTXAPostGenSweepMag (int channel, double mag);
|
||||
|
||||
public native void SetTXAPostGenSweepFreq (int channel, double freq1, double freq2);
|
||||
|
||||
public native void SetTXAPostGenSweepRate (int channel, double rate);
|
||||
|
||||
/**
|
||||
* PatchPanel
|
||||
*/
|
||||
|
||||
|
||||
public native void SetTXAPanelRun (int channel, int run);
|
||||
|
||||
public native void SetTXAPanelSelect (int channel, int select);
|
||||
|
||||
public native void SetTXAPanelGain1 (int channel, double gain);
|
||||
|
||||
/**
|
||||
* Noise Gate
|
||||
*/
|
||||
|
||||
public native void SetTXAAMSQRun (int channel, int run);
|
||||
|
||||
public native void SetTXAAMSQMutedGain (int channel, double dBlevel);
|
||||
|
||||
public native void SetTXAAMSQThreshold (int channel, double threshold);
|
||||
|
||||
/**
|
||||
* Equalizer
|
||||
*/
|
||||
|
||||
public native void SetTXAEQRun (int channel, int run);
|
||||
|
||||
public native void SetTXAEQWintype (int channel, int wintype);
|
||||
|
||||
public native void SetTXAEQCtfmode (int channel, int mode);
|
||||
|
||||
public native void SetTXAEQProfile (int channel, int nfreqs, double[] F, double[] G);
|
||||
|
||||
/**
|
||||
* FM Pre-emphasis
|
||||
*/
|
||||
|
||||
|
||||
public native void SetTXAFMEmphPosition (int channel, int position);
|
||||
|
||||
/**
|
||||
* Leveler
|
||||
*/
|
||||
|
||||
public native void SetTXALevelerSt (int channel, int state);
|
||||
|
||||
public native void SetTXALevelerAttack (int channel, int attack);
|
||||
|
||||
public native void SetTXALevelerDecay (int channel, int decay);
|
||||
|
||||
public native void SetTXALevelerTop (int channel, double maxgain);
|
||||
|
||||
/**
|
||||
* Phase Rotator
|
||||
*/
|
||||
|
||||
public native void SetTXAPHROTRun (int channel, int run);
|
||||
|
||||
public native void SetTXAPHROTCorner (int channel, double frequency);
|
||||
|
||||
public native void SetTXAPHROTNstages (int channel, int nstages);
|
||||
|
||||
|
||||
/**
|
||||
* Continuous Frequency Compressor (CFC)
|
||||
* */
|
||||
|
||||
public native void SetTXACFCOMPRun (int channel, int run);
|
||||
|
||||
public native void SetTXACFCOMPprofile (int channel, int nfreqs, double[] F, double[] G, double[] E);
|
||||
|
||||
public native void SetTXACFCOMPPrecomp (int channel, double precomp);
|
||||
|
||||
public native void SetTXACFCOMPPeqRun (int channel, int run);
|
||||
|
||||
public native void SetTXACFCOMPPrePeq (int channel, double prepeq);
|
||||
|
||||
|
||||
/**
|
||||
* Bandpass Filters
|
||||
*/
|
||||
|
||||
public native void SetTXABandpassFreqs (int channel, double f_low, double f_high);
|
||||
|
||||
public native void SetTXABandpassWindow (int channel, int wintype);
|
||||
|
||||
|
||||
/**
|
||||
* Speech Processor
|
||||
*/
|
||||
|
||||
public native void SetTXACompressorRun (int channel, int run);
|
||||
|
||||
public native void SetTXACompressorGain (int channel, double gain);
|
||||
|
||||
/**
|
||||
* CESSB Overshoot Control
|
||||
*/
|
||||
|
||||
public native void SetTXAosctrlRun (int channel, int run);
|
||||
|
||||
/**
|
||||
* ALC
|
||||
*/
|
||||
|
||||
public native void SetTXAALCSt (int channel, int state);
|
||||
|
||||
public native void SetTXAALCAttack (int channel, int attack);
|
||||
|
||||
public native void SetTXAALCDecay (int channel, int decay);
|
||||
|
||||
public native void SetTXAALCMaxGain (int channel, double maxgain);
|
||||
|
||||
/**
|
||||
* AM Modulator
|
||||
*/
|
||||
|
||||
public native void SetTXAAMCarrierLevel (int channel, double c_level);
|
||||
|
||||
/**
|
||||
* FM Modulator
|
||||
*/
|
||||
|
||||
public native void SetTXAFMDeviation (int channel, double deviation);
|
||||
|
||||
public native void SetTXACTCSSRun (int channel, int run);
|
||||
|
||||
public native void SetTXACTCSSFreq (int channel, double freq);
|
||||
|
||||
/**
|
||||
* Siphon
|
||||
*/
|
||||
|
||||
public native void TXASetSipMode (int channel, int mode);
|
||||
|
||||
public native void TXASetSipDisplay (int channel, int disp);
|
||||
|
||||
public native void TXAGetaSipF (int channel, float[] out, int size);
|
||||
|
||||
public native void TXAGetaSipF1 (int channel, float[] out, int size);
|
||||
|
||||
public native void TXAGetSpecF1 (int channel, float[] out);
|
||||
|
||||
public native void TXASetSipSpecmode (int channel, int mode);
|
||||
|
||||
/**
|
||||
* PureSignal I/Q Predistortion
|
||||
*/
|
||||
|
||||
//TODO
|
||||
|
||||
//CFIR Filter
|
||||
|
||||
public native void SetTXACFIRRun (int channel, int run);
|
||||
|
||||
/*
|
||||
TXA Collectives & General Controls
|
||||
*/
|
||||
public native void SetTXAMode (int channel, int mode);
|
||||
|
||||
public native void TXASetNC (int channel, int nc);
|
||||
|
||||
public native void TXASetMP (int channel, int mp);
|
||||
|
||||
/**
|
||||
* Panadapte & Other Frequency-Domain Displays
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Creating and Destroying a Display
|
||||
*/
|
||||
|
||||
public native void XCreateAnalyzer(int disp,
|
||||
int[] success,
|
||||
int m_size,
|
||||
int m_LO,
|
||||
int m_stitch,
|
||||
String app_data_path
|
||||
);
|
||||
|
||||
public native void DestroyAnalyzer(int disp);
|
||||
|
||||
|
||||
/**
|
||||
* Setting Display Parameters
|
||||
*/
|
||||
|
||||
public native void SetAnalyzer (
|
||||
int disp,
|
||||
int n_pixout,
|
||||
int n_fft,
|
||||
int typ,
|
||||
int[] flp,
|
||||
int sz,
|
||||
int bf_sz,
|
||||
int win_type,
|
||||
double pi,
|
||||
int ovrlp,
|
||||
int clp,
|
||||
double fscLin,
|
||||
double fscHin,
|
||||
int n_pix,
|
||||
int n_stch,
|
||||
int calset,
|
||||
double fmin,
|
||||
double fmax,
|
||||
int max_w);
|
||||
|
||||
|
||||
public native void SetCalibration (int disp, int set_num, int n_points, double cal[]);
|
||||
|
||||
|
||||
public native void SetDisplayDetectorMode (int disp, int pixout, int mode);
|
||||
|
||||
public native void SetDisplayAverageMode (int disp, int pixout, int mode);
|
||||
|
||||
public native void SetDisplayNumAverage (int disp, int pixout, int num);
|
||||
|
||||
public native void SetDisplayAvBackmult (int disp, int pixout, double mult);
|
||||
|
||||
public native void SetDisplaySampleRate (int disp, int rate);
|
||||
|
||||
public native void SetDisplayNormOneHz (int disp, int pixout, int norm);
|
||||
|
||||
/**
|
||||
* Supplying Input Data
|
||||
*/
|
||||
|
||||
public native void Spectrum2 (int run, int disp, int ss, int LO, double[] pbuff);
|
||||
|
||||
public native void Spectrum0 (int run, int disp, int ss, int LO, double[] pbuff);
|
||||
|
||||
public native void Spectrum (int disp, int ss, int LO, double[] pI, double[] pQ);
|
||||
|
||||
/**
|
||||
* Retrieving Pixel Values
|
||||
*/
|
||||
|
||||
public native void GetPixels (int disp, int pixout, float[] pix, int[] flag);
|
||||
|
||||
/**
|
||||
* Preemptive Wideband Noise Blanker
|
||||
*/
|
||||
|
||||
public native void create_anb (
|
||||
int run,
|
||||
int buffsize,
|
||||
double[] in,
|
||||
double[] out,
|
||||
double samplerate,
|
||||
double tau,
|
||||
double hangtime,
|
||||
double advtime,
|
||||
double backtau,
|
||||
double threshold);
|
||||
|
||||
//TODO additonal methods.
|
||||
|
||||
/**
|
||||
* Interpolating Wideband Noise Blanker
|
||||
*/
|
||||
|
||||
public native void create_nob (
|
||||
int run,
|
||||
int buffsize,
|
||||
double[] in,
|
||||
double[] out,
|
||||
double samplerate,
|
||||
int mode,
|
||||
double advslewtime,
|
||||
double advtime,
|
||||
double hangslewtime,
|
||||
double hangtime,
|
||||
double max_imp_seq_time,
|
||||
double backtau,
|
||||
double threshold);
|
||||
|
||||
//TODO additonal methods.
|
||||
|
||||
/**
|
||||
* Identifier-Based Calls
|
||||
*/
|
||||
|
||||
public native void create_nobEXT (
|
||||
int id,
|
||||
int run,
|
||||
int mode,
|
||||
int buffsize,
|
||||
double samplerate,
|
||||
double slewtime,
|
||||
double hangtime,
|
||||
double advtime,
|
||||
double backtau,
|
||||
double threshold);
|
||||
|
||||
public native void destroy_nobEXT (int id);
|
||||
|
||||
public native void flush_nobEXT (int id);
|
||||
|
||||
public native void xnobEXT (int id, double[] in, double[] out);
|
||||
|
||||
public native void SetEXTNOBRun (int id, int run);
|
||||
|
||||
public native void SetEXTNOBMode (int id, int mode);
|
||||
|
||||
public native void SetEXTNOBSamplerate (int id, int rate);
|
||||
|
||||
public native void SetEXTNOBBuffsize (int id, int size);
|
||||
|
||||
public native void SetEXTNOBTau (int id, double tau);
|
||||
|
||||
public native void SetEXTNOBHangtime (int id, double time);
|
||||
|
||||
public native void SetEXTNOBAdvtime (int id, double time);
|
||||
|
||||
public native void SetEXTNOBBacktau (int id, double tau);
|
||||
|
||||
public native void SetEXTNOBThreshold (int id, double thresh);
|
||||
|
||||
/**
|
||||
* Resampler
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//TODO there are more methods....
|
||||
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
|
||||
public static final int INACTIVE = 0;
|
||||
public static final int ACTIVE = 1;
|
||||
|
||||
public static final int OFF = 0;
|
||||
public static final int LONG = 1; // (hangtime = 2000ms, τ_decay = 2000ms)
|
||||
public static final int SLOW = 2; // (hangtime = 1000ms, τ_decay = 500ms)
|
||||
public static final int MED = 3; // (No Hang, τ_decay = 250ms)
|
||||
public static final int FAST = 4; // (No Hang, τ_decay = 50ms)
|
||||
public static final int CUSTOM = 5; // Settings
|
||||
|
||||
public static final int S_PK = 0;
|
||||
public static final int S_AV = 1;
|
||||
public static final int ADC_PK = 2;
|
||||
public static final int ADC_AV = 3;
|
||||
public static final int AGC_GAIN = 4;
|
||||
public static final int AGC_PK = 5;
|
||||
public static final int AGC_AV = 6;
|
||||
|
||||
public static final int REAL = 0;
|
||||
public static final int COMPLEX = 1;
|
||||
|
||||
public static final int RECTANGULAR = 0;
|
||||
public static final int BLACKMAN_HARRIS = 1;
|
||||
public static final int HANN = 2;
|
||||
public static final int FLAT_TOP = 3;
|
||||
public static final int HAMMING = 4;
|
||||
public static final int KAISER = 5;
|
||||
|
||||
public static final int PEAK_DETECT = -1;
|
||||
public static final int NO_AVERAGING = 0;
|
||||
public static final int TIME_WEIGHTED_LINEAR = 1;
|
||||
public static final int TIME_WEIGHTED_LOG = 2;
|
||||
public static final int WINDOW_LINEAR = 3;
|
||||
public static final int WINDOW_LOG = 4;
|
||||
public static final int WEIGHTED_LINEAR_LOW_NOISE = 5;
|
||||
public static final int WEIGHTED_LOG_LOW_NOISE = 6;
|
||||
}
|
||||
|
||||
//End of source.
|
||||
441
linux_port.c
Normal file
441
linux_port.c
Normal file
@ -0,0 +1,441 @@
|
||||
/* linux_port.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V and John Melton, G0ORX/N6LYT
|
||||
|
||||
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
|
||||
john.d.melton@googlemail.com
|
||||
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "linux_port.h"
|
||||
#include "comm.h"
|
||||
|
||||
/********************************************************************************************************
|
||||
* *
|
||||
* Linux Port Utilities *
|
||||
* *
|
||||
********************************************************************************************************/
|
||||
|
||||
#if defined(linux) || defined(__APPLE__)
|
||||
|
||||
void QueueUserWorkItem(void *function,void *context,int flags) {
|
||||
pthread_t t;
|
||||
pthread_create(&t, NULL, function, context);
|
||||
pthread_join(t, NULL);
|
||||
}
|
||||
|
||||
static inline void init_crit_section(pthread_mutex_t *mutex) {
|
||||
pthread_mutexattr_t mAttr;
|
||||
pthread_mutexattr_init(&mAttr);
|
||||
#ifdef __APPLE__
|
||||
// DL1YCF: MacOS X does not have PTHREAD_MUTEX_RECURSIVE_NP
|
||||
pthread_mutexattr_settype(&mAttr,PTHREAD_MUTEX_RECURSIVE);
|
||||
#else
|
||||
pthread_mutexattr_settype(&mAttr,PTHREAD_MUTEX_RECURSIVE_NP);
|
||||
#endif
|
||||
pthread_mutex_init(mutex,&mAttr);
|
||||
pthread_mutexattr_destroy(&mAttr);
|
||||
}
|
||||
|
||||
void InitializeCriticalSection(pthread_mutex_t *mutex) {
|
||||
init_crit_section(mutex);
|
||||
}
|
||||
|
||||
void InitializeCriticalSectionAndSpinCount(pthread_mutex_t *mutex, int count) {
|
||||
init_crit_section(mutex);
|
||||
}
|
||||
|
||||
void EnterCriticalSection(pthread_mutex_t *mutex) {
|
||||
pthread_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
void LeaveCriticalSection(pthread_mutex_t *mutex) {
|
||||
pthread_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
void DeleteCriticalSection(pthread_mutex_t *mutex) {
|
||||
pthread_mutex_destroy(mutex);
|
||||
}
|
||||
|
||||
int LinuxWaitForSingleObject(sem_t *sem,int ms) {
|
||||
int result=0;
|
||||
if(ms==INFINITE) {
|
||||
// wait for the lock
|
||||
result=sem_wait(sem);
|
||||
} else {
|
||||
for (int i = 0; i < ms; i++) {
|
||||
result=sem_trywait(sem);
|
||||
if (result == 0) break;
|
||||
Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
sem_t *LinuxCreateSemaphore(int attributes,int initial_count,int maximum_count,char *name) {
|
||||
sem_t *sem;
|
||||
#ifdef __APPLE__
|
||||
//
|
||||
// DL1YCF
|
||||
// This routine is usually invoked with name=NULL, so we have to make
|
||||
// a unique name of tpye WDSPxxxxxxxx for each invocation, since MacOS only
|
||||
// supports named semaphores. We shall unlink in due course, but first we
|
||||
// need to check whether the name is possibly already in use, e.g. by
|
||||
// another SDR program running on the same machine.
|
||||
//
|
||||
static long semcount=0;
|
||||
char sname[20];
|
||||
for (;;) {
|
||||
sprintf(sname,"WDSP%08ld",semcount++);
|
||||
sem=sem_open(sname, O_CREAT | O_EXCL, 0700, initial_count);
|
||||
if (sem == SEM_FAILED && errno == EEXIST) continue;
|
||||
break;
|
||||
}
|
||||
if (sem == SEM_FAILED) {
|
||||
perror("WDSP:CreateSemaphore");
|
||||
}
|
||||
//
|
||||
// we can unlink the semaphore NOW. It will remain functional
|
||||
// until sem_close() has been called by all threads using that
|
||||
// semaphore.
|
||||
//
|
||||
sem_unlink(sname);
|
||||
#else
|
||||
sem=malloc0(sizeof(sem_t));
|
||||
int result;
|
||||
// DL1YCF: added correct initial count
|
||||
result=sem_init(sem, 0, initial_count);
|
||||
if (result < 0) {
|
||||
perror("WDSP:CreateSemaphore");
|
||||
}
|
||||
#endif
|
||||
return sem;
|
||||
}
|
||||
|
||||
void LinuxReleaseSemaphore(sem_t* sem,int release_count, int* previous_count) {
|
||||
//
|
||||
// Note WDSP always calls this with previous_count==NULL
|
||||
// so we do not bother about obtaining the previous value and
|
||||
// storing it in *previous_count.
|
||||
//
|
||||
while(release_count>0) {
|
||||
sem_post(sem);
|
||||
release_count--;
|
||||
}
|
||||
}
|
||||
|
||||
sem_t *CreateEvent(void* security_attributes,int bManualReset,int bInitialState,char* name) {
|
||||
//
|
||||
// This is always called with bManualReset = bInitialState = FALSE
|
||||
//
|
||||
sem_t *sem;
|
||||
sem=LinuxCreateSemaphore(0,0,0,0);
|
||||
return sem;
|
||||
}
|
||||
|
||||
void LinuxSetEvent(sem_t* sem) {
|
||||
//
|
||||
// WDSP uses this to set the semaphore (event) to
|
||||
// a "releasing" state.
|
||||
// we simulate this by posting
|
||||
sem_post(sem);
|
||||
}
|
||||
|
||||
void LinuxResetEvent(sem_t* sem) {
|
||||
//
|
||||
// WDSP uses this to set the semaphore (event) to
|
||||
// a blocking state.
|
||||
// We mimic this by calling sem_trywait as long as it succeeds
|
||||
//
|
||||
while (sem_trywait(sem) == 0) ;
|
||||
}
|
||||
|
||||
HANDLE _beginthread( void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist) {
|
||||
pthread_t threadid;
|
||||
pthread_attr_t attr;
|
||||
|
||||
if (pthread_attr_init(&attr)) {
|
||||
return (HANDLE)-1;
|
||||
}
|
||||
|
||||
if(stack_size!=0) {
|
||||
if (pthread_attr_setstacksize(&attr, stack_size)) {
|
||||
return (HANDLE)-1;
|
||||
}
|
||||
}
|
||||
|
||||
if(pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED)) {
|
||||
return (HANDLE)-1;
|
||||
}
|
||||
|
||||
if (pthread_create(&threadid, &attr, (void*(*)(void*))start_address, arglist)) {
|
||||
return (HANDLE)-1;
|
||||
}
|
||||
|
||||
//pthread_attr_destroy(&attr);
|
||||
#if !defined(__APPLE__) && !defined(NO_PTHREAD_SETNAME_NP)
|
||||
//
|
||||
// pthread_setname_np does not exist (or exists with
|
||||
// different semantics) on MacOS and on certain
|
||||
// lightweight LINUX variants such as "DietPi".
|
||||
// Using pthread_setname_np() serves no function except that
|
||||
// one sees what the individual threads are doing when
|
||||
// watching the system via "top -h"
|
||||
//
|
||||
void sendbuf(void *arg); // declared in analyzer.c but not in header file
|
||||
char tname[64];
|
||||
if (start_address == &wdspmain) {
|
||||
snprintf(tname, sizeof(tname), "Wchan%d", (int)(uintptr_t)arglist);
|
||||
} else if (start_address == &sendbuf) {
|
||||
snprintf(tname, sizeof(tname), "Wdisp%d", (int)(uintptr_t)arglist);
|
||||
} else if (start_address == &flushChannel) {
|
||||
snprintf(tname, sizeof(tname), "Wflush%d", (int)(uintptr_t)arglist);
|
||||
} else if (start_address == &syncb_main) {
|
||||
snprintf(tname, sizeof(tname), "WSync");
|
||||
} else if (start_address == &doPSCalcCorrection
|
||||
|| start_address == &doPSTurnoff
|
||||
|| start_address == &PSSaveCorrection
|
||||
|| start_address == &PSRestoreCorrection) {
|
||||
snprintf(tname, sizeof(tname), "PURESIGNAL");
|
||||
} else {
|
||||
// in case there are more worker types
|
||||
snprintf(tname, sizeof(tname), "WDSP");
|
||||
}
|
||||
//
|
||||
// Ignore return value since we continue anyway.
|
||||
//
|
||||
(void) pthread_setname_np(threadid, tname);
|
||||
#endif
|
||||
|
||||
return (HANDLE)threadid;
|
||||
|
||||
}
|
||||
|
||||
void _endthread() {
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void SetThreadPriority(HANDLE thread, int priority) {
|
||||
//
|
||||
// In Linux, the scheduling priority only affects
|
||||
// real-time threads (SCHED_FIFO, SCHED_RR), so this
|
||||
// is basically a no-op here.
|
||||
//
|
||||
/*
|
||||
int policy;
|
||||
struct sched_param param;
|
||||
|
||||
pthread_getschedparam(thread, &policy, ¶m);
|
||||
param.sched_priority = sched_get_priority_max(policy);
|
||||
pthread_setschedparam(thread, policy, ¶m);
|
||||
*/
|
||||
}
|
||||
|
||||
void CloseHandle(HANDLE hObject) {
|
||||
//
|
||||
// This routine is *ONLY* called to release semaphores
|
||||
// The WDSP transmitter thread terminates upon each TX/RX
|
||||
// transition, where it closes and re-opens a semaphore
|
||||
// in flush_buffs() in iobuffs.c. Therefore, we have to
|
||||
// release any resource associated with this semaphore, which
|
||||
// may be a small memory patch (LINUX) or a file descriptor
|
||||
// (MacOS).
|
||||
//
|
||||
#ifdef __APPLE__
|
||||
if (sem_close((sem_t *)hObject) < 0) {
|
||||
perror("WDSP:CloseHandle:SemCLose");
|
||||
}
|
||||
#else
|
||||
if (sem_destroy((sem_t *)hObject) < 0) {
|
||||
perror("WDSP:CloseHandle:SemDestroy");
|
||||
} else {
|
||||
// if sem_destroy failed, do not release storage
|
||||
_aligned_free(hObject);
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MALLOC debug facility.
|
||||
// In the header file (linux_port.h), one can #define
|
||||
//
|
||||
// _aligned_malloc(a,b) ==> my_malloc(a)
|
||||
// _aligned_free(a) ==> my_free(a)
|
||||
//
|
||||
// Then all memory allocations/deallocations will be done via my_malloc() my_free()
|
||||
// Note this is thread-safe, since an explicit mutex is used.
|
||||
//
|
||||
// my_alloc will build a "fence", 1k wide, to both sides of the allocated area,
|
||||
// and fill it with some bit pattern.
|
||||
//
|
||||
// my_free will check for the integrity of the "fence" and report how many bytes
|
||||
// in the upper and lower fence have illegally been changed
|
||||
//
|
||||
// furthermore, my_free will complain (and terminate the program) if its argument
|
||||
// does not point to an active memory block allocated with my_malloc.
|
||||
//
|
||||
// P.S.1: Using "valgrind" with such time-critical programs is not a good idea,
|
||||
// so here is a solution.
|
||||
//
|
||||
// P.S.2: Further extensions are possible, e.g. include __FUNCTION__ and __LINE__
|
||||
// in the argument list of my_malloc(), store this in MEM_LIST, and report
|
||||
// upon failure.
|
||||
//
|
||||
// P.S.3: The standard definitions in linux_port.h are
|
||||
//
|
||||
// __aligned_malloc(a,b) ==> malloc(a)
|
||||
// __aligned_free(a) ==> free(a)
|
||||
//
|
||||
// and with these, "MALLOC debug" code is not used.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
struct _MEM_LIST {
|
||||
void *baseptr;
|
||||
void *freeptr;
|
||||
size_t size;
|
||||
int in_use;
|
||||
};
|
||||
|
||||
typedef struct _MEM_LIST MEM_LIST;
|
||||
|
||||
#define MEM_LIST_SIZE 32768
|
||||
|
||||
MEM_LIST malloc_slot[MEM_LIST_SIZE] = {0};
|
||||
|
||||
void *my_malloc(size_t size) {
|
||||
int slot;
|
||||
void *baseptr, *freeptr;;
|
||||
uint8_t *p1, *p2;
|
||||
|
||||
pthread_mutex_lock(&malloc_mutex);
|
||||
//
|
||||
// locate a free slot
|
||||
//
|
||||
slot=-1;
|
||||
for (int i=0; i<MEM_LIST_SIZE; i++) {
|
||||
if (malloc_slot[i].in_use == 0) {
|
||||
slot=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot < 0) {
|
||||
fprintf(stderr,"my_malloc: All Slots Exhausted.\n");
|
||||
fflush(stderr);
|
||||
pthread_mutex_unlock(&malloc_mutex);
|
||||
_exit(8);
|
||||
}
|
||||
baseptr=malloc(size+2048);
|
||||
if (baseptr == NULL) { return NULL; }
|
||||
|
||||
freeptr=baseptr+1024;
|
||||
|
||||
malloc_slot[slot].in_use = 1;
|
||||
malloc_slot[slot].baseptr = baseptr;
|
||||
malloc_slot[slot].freeptr = freeptr;
|
||||
malloc_slot[slot].size = size;
|
||||
|
||||
//
|
||||
// Create a "fence" around the allocated area
|
||||
//
|
||||
p1 = baseptr;
|
||||
p2 = freeptr + size;
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
*p1++ = 0xAA;
|
||||
*p1++ = 0x55;
|
||||
*p1++ = 0xEF;
|
||||
*p1++ = 0xFE;
|
||||
*p2++ = 0xAA;
|
||||
*p2++ = 0x55;
|
||||
*p2++ = 0xEF;
|
||||
*p2++ = 0xFE;
|
||||
}
|
||||
pthread_mutex_unlock(&malloc_mutex);
|
||||
//fprintf(stderr,"my_malloc: Allocated Block slot=%d addr=%p\n", slot, freeptr);
|
||||
return freeptr;
|
||||
}
|
||||
|
||||
void my_free(void *ptr) {
|
||||
int slot;
|
||||
uint8_t *p1, *p2;
|
||||
|
||||
pthread_mutex_lock(&malloc_mutex);
|
||||
//
|
||||
// Search for block
|
||||
//
|
||||
slot=-1;
|
||||
for (int i=0; i<4096; i++) {
|
||||
if (malloc_slot[i].in_use == 1 && malloc_slot[i].freeptr == ptr) {
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot < 0) {
|
||||
fprintf(stderr,"my_free: Trying to free non-allocated block at addr=%p\n",ptr);
|
||||
fflush(stderr);
|
||||
pthread_mutex_unlock(&malloc_mutex);
|
||||
_exit(8);
|
||||
}
|
||||
//
|
||||
// Verify integrity of fence
|
||||
//
|
||||
int under_count=0;
|
||||
int over_count=0;
|
||||
|
||||
p1 = malloc_slot[slot].baseptr;
|
||||
p2 = malloc_slot[slot].freeptr+malloc_slot[slot].size;
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
if (*p1++ != 0xAA) under_count++;
|
||||
if (*p1++ != 0x55) under_count++;
|
||||
if (*p1++ != 0xEF) under_count++;
|
||||
if (*p1++ != 0xFE) under_count++;
|
||||
if (*p2++ != 0xAA) over_count++;
|
||||
if (*p2++ != 0x55) over_count++;
|
||||
if (*p2++ != 0xEF) over_count++;
|
||||
if (*p2++ != 0xFE) over_count++;
|
||||
}
|
||||
if (under_count > 0) {
|
||||
fprintf(stderr,"WARNING: my_free: Fence underrun =%d\n", under_count);
|
||||
}
|
||||
if (over_count > 0) {
|
||||
fprintf(stderr,"WARNING: my_free: Fence overrun =%d\n", over_count);
|
||||
}
|
||||
if (over_count > 0 || under_count > 0) {
|
||||
fprintf(stderr,"WARNING: my_free: Block slot=%d size=%ld allocated addr=%p\n", slot,
|
||||
(long) malloc_slot[slot].size, malloc_slot[slot].freeptr);
|
||||
}
|
||||
free(malloc_slot[slot].baseptr);
|
||||
malloc_slot[slot].in_use=0;
|
||||
|
||||
pthread_mutex_unlock(&malloc_mutex);
|
||||
}
|
||||
|
||||
#endif
|
||||
135
linux_port.h
Normal file
135
linux_port.h
Normal file
@ -0,0 +1,135 @@
|
||||
/* linux_port.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V and John Melton, G0ORX/N6LYT
|
||||
|
||||
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
|
||||
john.d.melton@googlemail.com
|
||||
|
||||
*/
|
||||
|
||||
#if defined(linux) || defined(__APPLE__)
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fenv.h>
|
||||
|
||||
// WDSP relies on IEEE 754 semantics (0/0 = NaN, x/0 = Inf) rather than
|
||||
// SIGFPE trapping. On macOS, Cocoa enables FP exception trapping in all threads.
|
||||
// WDSP_FPE_GUARD saves the current FP environment and disables trapping;
|
||||
// WDSP_FPE_RESTORE puts it back. Place GUARD at the top and RESTORE at every
|
||||
// return path of any public WDSP function that does floating-point math.
|
||||
#ifdef __APPLE__
|
||||
# define WDSP_FPE_GUARD fenv_t _wdsp_fenv_; feholdexcept(&_wdsp_fenv_)
|
||||
# define WDSP_FPE_RESTORE fesetenv(&_wdsp_fenv_)
|
||||
#else
|
||||
# define WDSP_FPE_GUARD ((void)0)
|
||||
# define WDSP_FPE_RESTORE ((void)0)
|
||||
#endif
|
||||
|
||||
#define CRITICAL_SECTION pthread_mutex_t
|
||||
#define byte unsigned char
|
||||
#define String char *
|
||||
#define LONG long
|
||||
#define DWORD long
|
||||
#define HANDLE void *
|
||||
#define WINAPI
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
#define TEXT(x) x
|
||||
#define InterlockedIncrement(base) __sync_add_and_fetch(base,1L)
|
||||
#define InterlockedDecrement(base) __sync_sub_and_fetch(base,1L)
|
||||
|
||||
//#define InterlockedBitTestAndSet(base,bit) __sync_or_and_fetch(base,1L<<bit)
|
||||
//#define InterlockedBitTestAndReset(base,bit) __sync_and_and_fetch(base,~(1L<<bit))
|
||||
|
||||
#define InterlockedBitTestAndSet(base,bit) __sync_fetch_and_or(base,1L<<bit)
|
||||
#define InterlockedBitTestAndReset(base,bit) __sync_fetch_and_and(base,~(1L<<bit))
|
||||
|
||||
#define InterlockedExchange(target,value) __sync_lock_test_and_set(target,value)
|
||||
#define InterlockedAnd(base,mask) __sync_fetch_and_and(base,mask)
|
||||
#define _InterlockedAnd(base,mask) __sync_fetch_and_and(base,mask)
|
||||
#define __declspec(x)
|
||||
#define __cdecl
|
||||
#define __stdcall
|
||||
#define __forceinline
|
||||
|
||||
#define _aligned_malloc(x,y) malloc(x)
|
||||
#define _aligned_free(x) free(x)
|
||||
// Activate these for malloc debug
|
||||
//#define _aligned_malloc(x,y) my_malloc(x);
|
||||
//#define _aligned_free(x) my_free(x);
|
||||
|
||||
void *my_malloc(size_t size);
|
||||
void my_free(void *p);
|
||||
|
||||
#define freopen_s freopen
|
||||
#define min(x,y) (x<y?x:y)
|
||||
#define max(x,y) (x<y?y:x)
|
||||
#define THREAD_PRIORITY_HIGHEST 0
|
||||
|
||||
#define Sleep(ms) usleep((ms)*1000)
|
||||
|
||||
#define CreateSemaphore(a,b,c,d) LinuxCreateSemaphore(a,b,c,d)
|
||||
#define WaitForSingleObject(x, y) LinuxWaitForSingleObject(x, y)
|
||||
#define ReleaseSemaphore(x,y,z) LinuxReleaseSemaphore(x,y,z)
|
||||
#define SetEvent(x) LinuxSetEvent(x)
|
||||
#define ResetEvent(x) LinuxResetEvent(x)
|
||||
|
||||
#define INFINITE -1
|
||||
|
||||
void QueueUserWorkItem(void *function,void *context,int flags);
|
||||
|
||||
// these two functions are the same on LINUX
|
||||
void InitializeCriticalSection(pthread_mutex_t *mutex);
|
||||
void InitializeCriticalSectionAndSpinCount(pthread_mutex_t *mutex, int count);
|
||||
|
||||
void EnterCriticalSection(pthread_mutex_t *mutex);
|
||||
|
||||
void LeaveCriticalSection(pthread_mutex_t *mutex);
|
||||
|
||||
void DeleteCriticalSection(pthread_mutex_t *mutex);
|
||||
|
||||
|
||||
sem_t *LinuxCreateSemaphore(int attributes,int initial_count,int maximum_count,char *name);
|
||||
|
||||
int LinuxWaitForSingleObject(sem_t *sem,int x);
|
||||
|
||||
void LinuxReleaseSemaphore(sem_t *sem,int release_count, int* previous_count);
|
||||
|
||||
sem_t *CreateEvent(void* security_attributes,int bManualReset,int bInitialState,char* name);
|
||||
|
||||
void LinuxSetEvent(sem_t* sem);
|
||||
void LinuxResetEvent(sem_t* sem);
|
||||
|
||||
HANDLE _beginthread( void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist);
|
||||
|
||||
void _endthread();
|
||||
|
||||
void SetThreadPriority(HANDLE thread, int priority);
|
||||
|
||||
void CloseHandle(HANDLE hObject);
|
||||
|
||||
#endif
|
||||
|
||||
555
lmath.c
Normal file
555
lmath.c
Normal file
@ -0,0 +1,555 @@
|
||||
/* lmath.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 2016, 2023 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void 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 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 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 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];
|
||||
}
|
||||
|
||||
|
||||
BLDR create_builder(int points, int ints)
|
||||
{
|
||||
// for the create function, 'points' and 'ints' are the MAXIMUM values that will be encountered
|
||||
BLDR a = (BLDR)malloc0 (sizeof(bldr));
|
||||
a->catxy = (double*)malloc0(2 * points * sizeof(double));
|
||||
a->sx = (double*)malloc0( points * sizeof(double));
|
||||
a->sy = (double*)malloc0( points * sizeof(double));
|
||||
a->h = (double*)malloc0( ints * sizeof(double));
|
||||
a->p = (int*) malloc0( ints * sizeof(int));
|
||||
a->np = (int*) malloc0( ints * sizeof(int));
|
||||
a->taa = (double*)malloc0( ints * sizeof(double));
|
||||
a->tab = (double*)malloc0( ints * sizeof(double));
|
||||
a->tag = (double*)malloc0( ints * sizeof(double));
|
||||
a->tad = (double*)malloc0( ints * sizeof(double));
|
||||
a->tbb = (double*)malloc0( ints * sizeof(double));
|
||||
a->tbg = (double*)malloc0( ints * sizeof(double));
|
||||
a->tbd = (double*)malloc0( ints * sizeof(double));
|
||||
a->tgg = (double*)malloc0( ints * sizeof(double));
|
||||
a->tgd = (double*)malloc0( ints * sizeof(double));
|
||||
a->tdd = (double*)malloc0( ints * sizeof(double));
|
||||
int nsize = 3 * ints + 1;
|
||||
int intp1 = ints + 1;
|
||||
int intm1 = ints - 1;
|
||||
a->A = (double*)malloc0(intp1 * intp1 * sizeof(double));
|
||||
a->B = (double*)malloc0(intp1 * intp1 * sizeof(double));
|
||||
a->C = (double*)malloc0(intm1 * intp1 * sizeof(double));
|
||||
a->D = (double*)malloc0(intp1 * sizeof(double));
|
||||
a->E = (double*)malloc0(intp1 * intp1 * sizeof(double));
|
||||
a->F = (double*)malloc0(intm1 * intp1 * sizeof(double));
|
||||
a->G = (double*)malloc0(intp1 * sizeof(double));
|
||||
a->MAT = (double*)malloc0(nsize * nsize * sizeof(double));
|
||||
a->RHS = (double*)malloc0(nsize * sizeof(double));
|
||||
a->SLN = (double*)malloc0(nsize * sizeof(double));
|
||||
a->z = (double*)malloc0(intp1 * sizeof(double));
|
||||
a->zp = (double*)malloc0(intp1 * sizeof(double));
|
||||
a->wrk = (double*)malloc0(nsize * sizeof(double));
|
||||
a->ipiv = (int*) malloc0(nsize * sizeof(int));
|
||||
return a;
|
||||
}
|
||||
|
||||
void destroy_builder(BLDR a)
|
||||
{
|
||||
_aligned_free(a->ipiv);
|
||||
_aligned_free(a->wrk);
|
||||
_aligned_free(a->catxy);
|
||||
_aligned_free(a->sx);
|
||||
_aligned_free(a->sy);
|
||||
_aligned_free(a->h);
|
||||
_aligned_free(a->p);
|
||||
_aligned_free(a->np);
|
||||
|
||||
_aligned_free(a->taa);
|
||||
_aligned_free(a->tab);
|
||||
_aligned_free(a->tag);
|
||||
_aligned_free(a->tad);
|
||||
_aligned_free(a->tbb);
|
||||
_aligned_free(a->tbg);
|
||||
_aligned_free(a->tbd);
|
||||
_aligned_free(a->tgg);
|
||||
_aligned_free(a->tgd);
|
||||
_aligned_free(a->tdd);
|
||||
|
||||
_aligned_free(a->A);
|
||||
_aligned_free(a->B);
|
||||
_aligned_free(a->C);
|
||||
_aligned_free(a->D);
|
||||
_aligned_free(a->E);
|
||||
_aligned_free(a->F);
|
||||
_aligned_free(a->G);
|
||||
|
||||
_aligned_free(a->MAT);
|
||||
_aligned_free(a->RHS);
|
||||
_aligned_free(a->SLN);
|
||||
|
||||
_aligned_free(a->z);
|
||||
_aligned_free(a->zp);
|
||||
|
||||
_aligned_free(a);
|
||||
}
|
||||
|
||||
void flush_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 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 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 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 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 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;
|
||||
}
|
||||
93
lmath.h
Normal file
93
lmath.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* lmath.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2015, 2023 Warren Pratt, NR0V
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
extern void dR (int n, double* r, double* y, double* z);
|
||||
|
||||
extern void trI (
|
||||
int n,
|
||||
double* r,
|
||||
double* B,
|
||||
double* y,
|
||||
double* v,
|
||||
double* dR_z
|
||||
);
|
||||
|
||||
extern void asolve(int xsize, int asize, double* x, double* a, double* r, double* z);
|
||||
|
||||
extern void median(int n, double* a, double* med);
|
||||
|
||||
#ifndef _bldr_h
|
||||
#define _bldr_h
|
||||
|
||||
typedef struct _bldr
|
||||
{
|
||||
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;
|
||||
} bldr, *BLDR;
|
||||
|
||||
extern BLDR create_builder(int points, int ints);
|
||||
|
||||
extern void destroy_builder(BLDR a);
|
||||
|
||||
extern void flush_builder(BLDR a, int points, int ints);
|
||||
|
||||
extern void xbuilder(BLDR a, int points, double* x, double* y, int ints, double* t, int* info, double* c, double ptol);
|
||||
|
||||
extern int fcompare(const void* a, const void* b);
|
||||
|
||||
extern void decomp(int n, double* a, int* piv, int* info, double* wrk);
|
||||
|
||||
extern void dsolve(int n, double* a, int* piv, double* b, double* x);
|
||||
|
||||
#endif
|
||||
177
main.c
Normal file
177
main.c
Normal file
@ -0,0 +1,177 @@
|
||||
/* main.c
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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.h"
|
||||
|
||||
void wdspmain (void *pargs)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
DWORD taskIndex = 0;
|
||||
HANDLE hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
|
||||
if (hTask != 0) AvSetMmThreadPriority(hTask, 2);
|
||||
else SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
#endif
|
||||
|
||||
int channel = (int)(uintptr_t)pargs;
|
||||
while (_InterlockedAnd (&ch[channel].run, 1))
|
||||
{
|
||||
WaitForSingleObject(ch[channel].iob.pd->Sem_BuffReady,INFINITE);
|
||||
EnterCriticalSection (&ch[channel].csDSP);
|
||||
if (!_InterlockedAnd (&ch[channel].iob.pd->exec_bypass, 1))
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0: // rxa
|
||||
dexchange (channel, rxa[channel].outbuff, rxa[channel].inbuff);
|
||||
xrxa (channel);
|
||||
break;
|
||||
case 1: // txa
|
||||
dexchange (channel, txa[channel].outbuff, txa[channel].inbuff);
|
||||
xtxa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection (&ch[channel].csDSP);
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
if (hTask != 0) AvRevertMmThreadCharacteristics (hTask);
|
||||
#endif
|
||||
}
|
||||
|
||||
void create_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
create_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
create_txa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void destroy_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
destroy_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
destroy_txa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void flush_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
flush_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
flush_txa (channel);
|
||||
break;
|
||||
case 31:
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setInputSamplerate_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
setInputSamplerate_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
setInputSamplerate_txa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setOutputSamplerate_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
setOutputSamplerate_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
setOutputSamplerate_txa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setDSPSamplerate_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
setDSPSamplerate_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
setDSPSamplerate_txa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setDSPBuffsize_main (int channel)
|
||||
{
|
||||
switch (ch[channel].type)
|
||||
{
|
||||
case 0:
|
||||
setDSPBuffsize_rxa (channel);
|
||||
break;
|
||||
case 1:
|
||||
setDSPBuffsize_txa (channel);
|
||||
break;
|
||||
case 31: //
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
46
main.h
Normal file
46
main.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* main.h
|
||||
|
||||
This file is part of a program that implements a Software-Defined Radio.
|
||||
|
||||
Copyright (C) 2013 Warren Pratt, NR0V
|
||||
|
||||
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 _mainloop_h
|
||||
#define _mainloop_h
|
||||
|
||||
extern void wdspmain (void *pargs);
|
||||
|
||||
extern void create_main (int channel);
|
||||
|
||||
extern void destroy_main (int channel);
|
||||
|
||||
extern void flush_main (int channel);
|
||||
|
||||
extern void setInputSamplerate_main (int channel);
|
||||
|
||||
extern void setOutputSamplerate_main (int channel);
|
||||
|
||||
extern void setDSPSamplerate_main (int channel);
|
||||
|
||||
extern void setDSPBuffsize_main (int channel);
|
||||
|
||||
#endif
|
||||
66
make_calculus.c
Normal file
66
make_calculus.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
115
make_interface.c
Normal file
115
make_interface.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
usage: make_interface *.c
|
||||
*/
|
||||
|
||||
|
||||
#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,rc,len;
|
||||
int first_in_file;
|
||||
int first_in_decl;
|
||||
char line[1000];
|
||||
size_t linesize=999;
|
||||
char *buffer=line;
|
||||
char *needle;
|
||||
char shipout[1000];
|
||||
|
||||
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) {
|
||||
int pos = 4;
|
||||
if (line[4] == ' ') pos++;
|
||||
printf("extern %s ", line+pos);
|
||||
} 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 {
|
||||
needle = strstr(line, "()");
|
||||
if (needle) {
|
||||
*needle = 0;
|
||||
snprintf(shipout, sizeof(shipout), "%s(void)%s", line, needle+2);
|
||||
} else {
|
||||
snprintf(shipout, sizeof(shipout), "%s", line);
|
||||
}
|
||||
if (first_in_decl) {
|
||||
printf("%s", shipout);
|
||||
first_in_decl=0;
|
||||
} else {
|
||||
printf("\n%s", shipout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
131
make_zetahat.c
Normal file
131
make_zetahat.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* make_zetahat
|
||||
*
|
||||
* This program reads the contents of the binary WDSP file "zetaHat.bin"
|
||||
* and prints the data.
|
||||
*
|
||||
* The output is intended to be part of the file "zetaHat.c" which
|
||||
* initializes these arrays (static data) for use with "memcpy"
|
||||
* in emnr.c.
|
||||
*
|
||||
* Should the WDSP file "zetaHat.bin" be changed, "zetaHat.c" must
|
||||
* be re-generated using this program.
|
||||
*
|
||||
* return values of main()
|
||||
*
|
||||
* 0 all OK
|
||||
* -1 sizeof(double) is not 8
|
||||
* -2 error opening file "zetaHat.bin"
|
||||
* -3 read error
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main() {
|
||||
int fd;
|
||||
int i,j;
|
||||
double d;
|
||||
const size_t dsize = sizeof(double);
|
||||
const size_t isize = sizeof(int);
|
||||
int rows, cols;
|
||||
double gmin, gmax, ximin, ximax;
|
||||
double *zetaDouble;
|
||||
int *zetaInt;
|
||||
|
||||
|
||||
if (dsize != 8) {
|
||||
printf("Data type DOUBLE is not 8-byte. Please check!\n");
|
||||
return -1;
|
||||
}
|
||||
if (isize != 4) {
|
||||
printf("Data type INT is not 4-byte. Please check!\n");
|
||||
return -1;
|
||||
}
|
||||
fd=open ("zetaHat.bin", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("Could not open file 'zetaHat.bin'\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (read(fd, &rows,isize) != isize) {
|
||||
printf("READ ERROR rows\n");
|
||||
return -3;
|
||||
}
|
||||
printf("int zetaHatDefaultRows = %d;\n", rows);
|
||||
|
||||
if (read(fd, &cols,isize) != isize) {
|
||||
printf("READ ERROR cols\n");
|
||||
return -3;
|
||||
}
|
||||
printf("int zetaHatDefaultCols = %d;\n", cols);
|
||||
|
||||
if (read(fd, &gmin, dsize) != dsize) {
|
||||
printf("READ ERROR gmin\n");
|
||||
return -3;
|
||||
}
|
||||
printf("double zetaHatDefaultGmin = %30.25f;\n", gmin);
|
||||
|
||||
if (read(fd, &gmax, dsize) != dsize) {
|
||||
printf("READ ERROR gmax\n");
|
||||
return -3;
|
||||
}
|
||||
printf("double zetaHatDefaultGmax = %30.25f;\n", gmax);
|
||||
|
||||
if (read(fd, &ximin, dsize) != dsize) {
|
||||
printf("READ ERROR ximin\n");
|
||||
return -3;
|
||||
}
|
||||
printf("double zetaHatDefaultXimin = %30.25f;\n", ximin);
|
||||
|
||||
if (read(fd, &ximax, dsize) != dsize) {
|
||||
printf("READ ERROR ximax\n");
|
||||
return -3;
|
||||
}
|
||||
printf("double zetaHatDefaultXimax = %30.25f;\n", ximax);
|
||||
|
||||
zetaDouble = malloc(rows*cols*dsize);
|
||||
if (zetaDouble == NULL) {
|
||||
printf("MALLOC ERROR Double\n");
|
||||
}
|
||||
zetaInt = malloc(rows*cols*isize);
|
||||
if (zetaInt == NULL) {
|
||||
printf("MALLOC ERROR Int\n");
|
||||
}
|
||||
|
||||
if (read(fd, zetaDouble, rows*cols*dsize) != rows*cols*dsize) {
|
||||
printf("READ ERROR in zetaHatDouble\n");
|
||||
}
|
||||
if (read(fd, zetaInt, rows*cols*isize) != rows*cols*isize) {
|
||||
printf("READ ERROR in zetaHatInt\n");
|
||||
}
|
||||
|
||||
//
|
||||
// ZetaDouble data is only valid where ZetaInt is non-negative.
|
||||
// So report a zero where this is the case
|
||||
//
|
||||
for (i=0; i< rows*cols; i++) {
|
||||
if (zetaInt[i] < 0) zetaDouble[i]=0.0;
|
||||
}
|
||||
|
||||
printf("double zetaHatDefaultData[%d]={\n", rows*cols);
|
||||
for (i=0; i<rows*cols-1; i++) {
|
||||
printf("%30.25f,", zetaDouble[i]);
|
||||
if (i % 4 == 3) printf("\n");
|
||||
}
|
||||
printf("%30.25f\n};\n", zetaDouble[rows*cols-1]);
|
||||
|
||||
printf("int zetaHatDefaultValid[%d]={\n", rows*cols);
|
||||
for (i=0; i<rows*cols-1; i++) {
|
||||
printf("%3d,", zetaInt[i]);
|
||||
if (i % 30 == 29) printf("\n");
|
||||
}
|
||||
printf("%3d\n};\n", zetaInt[rows*cols-1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user