├── example ├── x10rf.wav ├── ufsk1200.raw ├── README-dumpcsv.txt ├── dumpcsv_png.txt └── multipager.py ├── msvc_support.h ├── .github ├── workflows │ ├── cmake.yml │ └── qmake.yml └── dependabot.yml ├── BCHCode.h ├── .gitignore ├── gen-ng.pro ├── unsupported ├── multimonNG.sln ├── Makefile.legacy └── multimonNG.vcxproj ├── BCHCode_stub.c ├── gen_sin.c ├── demod_eia.c ├── demod_eea.c ├── demod_ccir.c ├── demod_dzvei.c ├── demod_pzvei.c ├── demod_zvei1.c ├── demod_zvei2.c ├── demod_zvei3.c ├── demod_dumpcsv.c ├── mkcostab.c ├── win32_soundin.c ├── demod_poc24.c ├── multimon-ng.pro ├── demod_display.c ├── gen_zvei.c ├── gen_dtmf.c ├── README.md ├── demod_poc5.c ├── demod_poc12.c ├── CMakeLists.txt ├── demod_hapn48.c ├── gen.h ├── uart.c ├── filter.h ├── demod_ufsk12.c ├── demod_clipfsk.c ├── demod_afsk24.c ├── demod_afsk24_2.c ├── demod_afsk24_3.c ├── gen_uart.c ├── gen_clipfsk.c ├── demod_fmsfsk.c ├── demod_afsk12.c ├── demod_fsk96.c ├── selcall.c ├── win32_getopt.h ├── demod_dtmf.c ├── multimon-ng.1 ├── demod_x10.c ├── gen_hdlc.c ├── filter-i386.h ├── costabi.c ├── xdisplay.c └── multimon.h /example/x10rf.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EliasOenal/multimon-ng/HEAD/example/x10rf.wav -------------------------------------------------------------------------------- /example/ufsk1200.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EliasOenal/multimon-ng/HEAD/example/ufsk1200.raw -------------------------------------------------------------------------------- /msvc_support.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define inline __inline 4 | 5 | #include "win32_getopt.h" 6 | 7 | #define strcasecmp(s1, s2) _stricmp(s1, s2) -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI cmake 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v6 12 | - name: cmake 13 | run: mkdir build && cd build && cmake .. 14 | - name: make 15 | run: cd build && make 16 | -------------------------------------------------------------------------------- /BCHCode.h: -------------------------------------------------------------------------------- 1 | struct BCHCode; 2 | 3 | struct BCHCode * BCHCode_New(int p[], int m, int n, int k, int t); 4 | void BCHCode_Delete(struct BCHCode * BCHCode_data); 5 | void BCHCode_Encode(struct BCHCode * BCHCode_data, int data[]); 6 | int BCHCode_Decode(struct BCHCode * BCHCode_data, int recd[]); 7 | -------------------------------------------------------------------------------- /.github/workflows/qmake.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI qmake 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v6 12 | - uses: jurplel/install-qt-action@v4 13 | - name: qmake 14 | run: mkdir build && cd build && qmake ../multimon-ng.pro 15 | -------------------------------------------------------------------------------- /example/README-dumpcsv.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | To generate a png from a wav file 5 | 6 | generate a .csv file from the data : 7 | 8 | multimon-ng -q -t wav -a DUMPCSV x10rf.wav > x10rf.csv 9 | 10 | use gnuplot to generate a PNG of the data 11 | 12 | gnuplot -e "plot_data_file='x10rf'" dumpcsv_png.txt 13 | 14 | there should be a file named x10rf.png 15 | 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 1000 13 | -------------------------------------------------------------------------------- /example/dumpcsv_png.txt: -------------------------------------------------------------------------------- 1 | set terminal png size 2600,500 2 | #set terminal png size 1920,500 3 | #set terminal png size 800,150 4 | set datafile separator "," 5 | set xlabel "Time (ms)" 6 | set ylabel "Arbitrary Power (dB?)" 7 | set grid 8 | #show mxtics 9 | set xtics 0.5 rotate 10 | set mxtics 10 11 | set key off 12 | set pointsize 0.5 13 | 14 | name=plot_data_file 15 | 16 | set title name." multimon-ng dump" 17 | # name=system("echo $plot_data_file") 18 | 19 | set output name.".png" 20 | 21 | plot name.".csv" using 1:2 with linespoints pt 6 lc 3 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | bin-* 10 | build 11 | 12 | # Packages # 13 | ############ 14 | # it's better to unpack these files and commit the raw source 15 | # git has its own built in compression methods 16 | *.7z 17 | *.dmg 18 | *.gz 19 | *.iso 20 | *.jar 21 | *.rar 22 | *.tar 23 | *.zip 24 | 25 | # Logs and databases # 26 | ###################### 27 | *.log 28 | *.sql 29 | *.sqlite 30 | 31 | # OS generated files # 32 | ###################### 33 | .DS_Store 34 | .DS_Store? 35 | ._* 36 | .Spotlight-V100 37 | .Trashes 38 | Icon? 39 | ehthumbs.db 40 | Thumbs.db 41 | *.bak 42 | 43 | # Tool cache # 44 | ############## 45 | *.sw[op] 46 | -------------------------------------------------------------------------------- /gen-ng.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= qt 4 | CONFIG -= app_bundle 5 | DEFINES += MAX_VERBOSE_LEVEL=1 6 | 7 | isEmpty(PREFIX) { 8 | PREFIX = /usr/local/ 9 | } 10 | TARGET = gen-ng 11 | target.path = $$PREFIX/bin 12 | INSTALLS += target 13 | 14 | HEADERS += \ 15 | gen.h 16 | 17 | 18 | SOURCES += \ 19 | gen.c \ 20 | gen_dtmf.c \ 21 | gen_sin.c \ 22 | gen_zvei.c \ 23 | gen_hdlc.c \ 24 | gen_uart.c \ 25 | gen_clipfsk.c \ 26 | costabi.c 27 | 28 | macx{ 29 | DEFINES += DUMMY_AUDIO 30 | DEFINES += ONLY_RAW 31 | DEFINES += CHARSET_UTF8 32 | } 33 | 34 | win32{ 35 | DEFINES += DUMMY_AUDIO 36 | DEFINES += ONLY_RAW 37 | DEFINES += WINDOWS 38 | LIBS += -lwinmm 39 | } 40 | 41 | 42 | unix:linux-g++-32:!symbian:!macx{ 43 | DEFINES += CHARSET_UTF8 44 | } 45 | 46 | unix:linux-g++-64:!symbian:!macx{ 47 | DEFINES += CHARSET_UTF8 48 | } 49 | 50 | unix:linux-g++:!symbian:!macx{ 51 | DEFINES += CHARSET_UTF8 52 | } 53 | -------------------------------------------------------------------------------- /unsupported/multimonNG.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "multimonNG", "multimonNG.vcxproj", "{135F6E37-CB51-7ED2-DB6B-177C50B9DBE1}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {135F6E37-CB51-7ED2-DB6B-177C50B9DBE1}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {135F6E37-CB51-7ED2-DB6B-177C50B9DBE1}.Debug|Win32.Build.0 = Debug|Win32 14 | {135F6E37-CB51-7ED2-DB6B-177C50B9DBE1}.Release|Win32.ActiveCfg = Release|Win32 15 | {135F6E37-CB51-7ED2-DB6B-177C50B9DBE1}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /BCHCode_stub.c: -------------------------------------------------------------------------------- 1 | /* 2 | * BCHCode_stub.c 3 | * 4 | * Copyright (C) 2018 Göran Weinholt 5 | * 6 | * Stub replacement for BCHCode.c, whose license is GPL incompatible 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this program; if not, write to the Free Software Foundation, Inc., 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include 24 | #include "BCHCode.h" 25 | 26 | struct BCHCode { 27 | }; 28 | 29 | struct BCHCode *BCHCode_New(int p[], int m, int n, int k, int t) 30 | { 31 | /* Ignore unused variables */ 32 | (void) p; 33 | (void) m; 34 | (void) n; 35 | (void) k; 36 | (void) t; 37 | return malloc(sizeof(struct BCHCode)); 38 | } 39 | 40 | void BCHCode_Delete(struct BCHCode *BCHCode_data) 41 | { 42 | free(BCHCode_data); 43 | } 44 | 45 | int BCHCode_Decode(struct BCHCode *BCHCode_data, int recd[]) 46 | { 47 | (void) BCHCode_data; 48 | (void) recd; 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /gen_sin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gen_sine.c -- generate DTMF sequences 3 | * 4 | * Copyright (C) 1997 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "gen.h" 25 | #include 26 | 27 | /* ---------------------------------------------------------------------- */ 28 | 29 | void gen_init_sine(struct gen_params *p, struct gen_state *s) 30 | { 31 | memset(s, 0, sizeof(struct gen_state)); 32 | s->s.sine.ph = 0; 33 | s->s.sine.phinc = (float)0x10000 * p->p.sine.freq / SAMPLE_RATE; 34 | s->s.sine.time = p->p.sine.duration; 35 | } 36 | 37 | int gen_sine(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s) 38 | { 39 | int num = 0; 40 | 41 | for (; (buflen > 0) && (s->s.sine.time > 0); buflen--, buf++, num++, s->s.sine.time--) { 42 | *buf += (p->ampl * COS(s->s.sine.ph)) >> 15; 43 | s->s.sine.ph += s->s.sine.phinc; 44 | } 45 | return num; 46 | } 47 | -------------------------------------------------------------------------------- /demod_eia.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_eia.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int eia_freq[16] = { 28 | PHINC(600), PHINC(741), PHINC(882), PHINC(1023), 29 | PHINC(1164), PHINC(1305), PHINC(1446), PHINC(1587), 30 | PHINC(1728), PHINC(1869), PHINC(2151), PHINC(2433), 31 | PHINC(2010), PHINC(2292), PHINC(459), PHINC(1091) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void eia_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void eia_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void eia_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, eia_freq, demod_eia.name); 49 | } 50 | 51 | const struct demod_param demod_eia = { 52 | "EIA", true, SAMPLE_RATE, 0, eia_init, eia_demod, eia_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_eea.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_eea.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int eea_freq[16] = { 28 | PHINC(1981), PHINC(1124), PHINC(1197), PHINC(1275), 29 | PHINC(1358), PHINC(1446), PHINC(1540), PHINC(1640), 30 | PHINC(1747), PHINC(1860), PHINC(1055), PHINC(930), 31 | PHINC(2400), PHINC(991), PHINC(2110), PHINC(2247) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void eea_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void eea_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void eea_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, eea_freq, demod_eea.name); 49 | } 50 | 51 | const struct demod_param demod_eea = { 52 | "EEA", true, SAMPLE_RATE, 0, eea_init, eea_demod, eea_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_ccir.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_ccir.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int ccir_freq[16] = { 28 | PHINC(1981), PHINC(1124), PHINC(1197), PHINC(1275), 29 | PHINC(1358), PHINC(1446), PHINC(1540), PHINC(1640), 30 | PHINC(1747), PHINC(1860), PHINC(2400), PHINC(930), 31 | PHINC(2247), PHINC(991), PHINC(2110), PHINC(1055) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void ccir_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void ccir_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void ccir_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, ccir_freq, demod_ccir.name); 49 | } 50 | 51 | const struct demod_param demod_ccir = { 52 | "CCIR", true, SAMPLE_RATE, 0, ccir_init, ccir_demod, ccir_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_dzvei.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_dzvei.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int dzvei_freq[16] = { 28 | PHINC(2200), PHINC(970), PHINC(1060), PHINC(1160), 29 | PHINC(1270), PHINC(1400), PHINC(1530), PHINC(1670), 30 | PHINC(1830), PHINC(2000), PHINC(825), PHINC(740), 31 | PHINC(2600), PHINC(885), PHINC(2400), PHINC(680) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void dzvei_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void dzvei_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void dzvei_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, dzvei_freq, demod_dzvei.name); 49 | } 50 | 51 | const struct demod_param demod_dzvei = { 52 | "DZVEI", true, SAMPLE_RATE, 0, dzvei_init, dzvei_demod, dzvei_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_pzvei.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_pzvei.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int pzvei_freq[16] = { 28 | PHINC(2400), PHINC(1060), PHINC(1160), PHINC(1270), 29 | PHINC(1400), PHINC(1530), PHINC(1670), PHINC(1830), 30 | PHINC(2000), PHINC(2200), PHINC(970), PHINC(810), 31 | PHINC(2800), PHINC(885), PHINC(2400), PHINC(680) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void pzvei_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void pzvei_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void pzvei_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, pzvei_freq, demod_pzvei.name); 49 | } 50 | 51 | const struct demod_param demod_pzvei = { 52 | "PZVEI", true, SAMPLE_RATE, 0, pzvei_init, pzvei_demod, pzvei_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_zvei1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_zvei1.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int zvei1_freq[16] = { 28 | PHINC(2400), PHINC(1060), PHINC(1160), PHINC(1270), 29 | PHINC(1400), PHINC(1530), PHINC(1670), PHINC(1830), 30 | PHINC(2000), PHINC(2200), PHINC(2800), PHINC(810), 31 | PHINC(970), PHINC(885), PHINC(2600), PHINC(680) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void zvei1_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void zvei1_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void zvei1_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, zvei1_freq, demod_zvei1.name); 49 | } 50 | 51 | const struct demod_param demod_zvei1 = { 52 | "ZVEI1", true, SAMPLE_RATE, 0, zvei1_init, zvei1_demod, zvei1_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_zvei2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_zvei2.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int zvei2_freq[16] = { 28 | PHINC(2400), PHINC(1060), PHINC(1160), PHINC(1270), 29 | PHINC(1400), PHINC(1530), PHINC(1670), PHINC(1830), 30 | PHINC(2000), PHINC(2200), PHINC(885), PHINC(825), 31 | PHINC(740), PHINC(680), PHINC(970), PHINC(2600) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void zvei2_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void zvei2_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void zvei2_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, zvei2_freq, demod_zvei2.name); 49 | } 50 | 51 | const struct demod_param demod_zvei2 = { 52 | "ZVEI2", true, SAMPLE_RATE, 0, zvei2_init, zvei2_demod, zvei2_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_zvei3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_zvei3.c 3 | * 4 | * Copyright (C) 2013 5 | * Elias Oenal (EliasOenal@gmail.com) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #define SAMPLE_RATE 22050 23 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 24 | 25 | #include "multimon.h" 26 | 27 | static const unsigned int zvei3_freq[16] = { 28 | PHINC(2400), PHINC(1060), PHINC(1160), PHINC(1270), 29 | PHINC(1400), PHINC(1530), PHINC(1670), PHINC(1830), 30 | PHINC(2000), PHINC(2200), PHINC(885), PHINC(810), 31 | PHINC(2800), PHINC(680), PHINC(970), PHINC(2600) 32 | }; 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | static void zvei3_init(struct demod_state *s) 37 | { 38 | selcall_init(s); 39 | } 40 | 41 | static void zvei3_deinit(struct demod_state *s) 42 | { 43 | selcall_deinit(s); 44 | } 45 | 46 | static void zvei3_demod(struct demod_state *s, buffer_t buffer, int length) 47 | { 48 | selcall_demod(s, buffer.fbuffer, length, zvei3_freq, demod_zvei3.name); 49 | } 50 | 51 | const struct demod_param demod_zvei3 = { 52 | "ZVEI3", true, SAMPLE_RATE, 0, zvei3_init, zvei3_demod, zvei3_deinit 53 | }; 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demod_dumpcsv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_dumpcsv.c -- dump data in CSV format 3 | * 4 | * Written and placed into the public domain by 5 | * Peter Shipley < peter.shipley@gmail.com > 6 | */ 7 | 8 | 9 | /* 10 | * Useful for piping to gnuplot 11 | * format is in time,value 12 | * where : 13 | * time is a float in ms 14 | * value is a float from the current data stream 15 | * 16 | */ 17 | 18 | /* ---------------------------------------------------------------------- */ 19 | 20 | #include "multimon.h" 21 | #include 22 | #include 23 | 24 | /* ---------------------------------------------------------------------- */ 25 | 26 | #define SAMPLING_RATE 22050 27 | #define SAMPLE_MS 22.050f 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | static void dumpcsv_init(struct demod_state *s) 32 | { 33 | memset(&s->l1.dumpcsv, 0, sizeof(s->l1.dumpcsv)); 34 | } 35 | 36 | /* ---------------------------------------------------------------------- */ 37 | 38 | 39 | static void dumpcsv_demod(struct demod_state *s, buffer_t buffer, int length) 40 | { 41 | const short *src; 42 | float f; 43 | int i; 44 | 45 | verbprintf(2, "dump_demod length=%d, current_sequence=%d\n", length, s->l1.dumpcsv.current_sequence); 46 | 47 | 48 | src = buffer.sbuffer; 49 | for ( i=0 ; i < length ; i++, src++) { 50 | f = (float) ( (i + s->l1.dumpcsv.current_sequence) / SAMPLE_MS ); 51 | 52 | /* cut back on superfluous plot points 53 | if (i > 0 && abs(src[-1] - *src ) < 40 ) 54 | continue; 55 | */ 56 | 57 | fprintf(stdout, "%.6f,%hd\n", f, *src); 58 | } 59 | 60 | // Save current count 61 | s->l1.dumpcsv.current_sequence = s->l1.dumpcsv.current_sequence + i; 62 | 63 | } 64 | 65 | /* ---------------------------------------------------------------------- */ 66 | 67 | const struct demod_param demod_dumpcsv = { 68 | "DUMPCSV", false, SAMPLING_RATE, 0, dumpcsv_init, dumpcsv_demod, NULL 69 | }; 70 | 71 | 72 | /* ---------------------------------------------------------------------- */ 73 | -------------------------------------------------------------------------------- /mkcostab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mkcostab.c -- cosine table generator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | /* ---------------------------------------------------------------------- */ 29 | 30 | #define COSTABSIZE 0x400 31 | 32 | /* ---------------------------------------------------------------------- */ 33 | 34 | int main(int argc, char *argv[]) 35 | { 36 | int i; 37 | FILE *fi, *ff; 38 | float f; 39 | 40 | if (!(fi = fopen("costabi.c", "w"))) 41 | exit(1); 42 | if (!(ff = fopen("costabf.c", "w"))) 43 | exit(1); 44 | fprintf(fi, "/*\n * This file is machine generated, DO NOT EDIT!\n */\n\n" 45 | "const int costabi[0x%x] = {", COSTABSIZE); 46 | fprintf(ff, "/*\n * This file is machine generated, DO NOT EDIT!\n */\n\n" 47 | "const float costabf[0x%x] = {", COSTABSIZE); 48 | for (i = 0; i < COSTABSIZE; i++) { 49 | if ((i & 3) == 0) 50 | fprintf(ff, "\n\t"); 51 | if ((i & 7) == 0) 52 | fprintf(fi, "\n\t"); 53 | f = cos(M_PI*2.0*i/COSTABSIZE); 54 | fprintf(ff, "%12.9f", f); 55 | fprintf(fi, "%6i", (int)(32767.0*f)); 56 | if (i < COSTABSIZE-1) { 57 | fprintf(ff, ", "); 58 | fprintf(fi, ", "); 59 | } 60 | } 61 | fprintf(ff, "\n};\n"); 62 | fprintf(fi, "\n};\n"); 63 | exit(0); 64 | } 65 | -------------------------------------------------------------------------------- /win32_soundin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define BUFFER_LEN_IN_MS 20 5 | #define BUFFERS 50 6 | 7 | int SAMPLES_PER_BUFFER; 8 | WAVEFORMATEX g_WavFmt = {0}; 9 | HWAVEIN hWavIn; 10 | 11 | void process_buffer(float *float_buf, short *short_buf, unsigned int len); 12 | int Overlap; 13 | float *fbuf; 14 | 15 | void CALLBACK waveInProc(HWAVEIN hwi,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2) 16 | { 17 | WAVEHDR* pWaveHdr; 18 | SHORT *sp; 19 | switch(uMsg) 20 | { 21 | case MM_WIM_DATA: 22 | pWaveHdr = ((WAVEHDR*)dwParam1 ); 23 | sp = (SHORT*)pWaveHdr->lpData; 24 | int i; 25 | for (i=0; ilpData = (LPSTR)pBuffer; 75 | WavHdr->dwBufferLength = SAMPLES_PER_BUFFER*sizeof(SHORT); 76 | WavHdr->dwBytesRecorded = 0; 77 | WavHdr->dwUser = 0; 78 | WavHdr->dwFlags = 0; 79 | WavHdr->dwLoops = 1; 80 | WavHdr->lpNext = 0; 81 | WavHdr->reserved = 0; 82 | MMRESULT x1= waveInPrepareHeader(hWavIn,WavHdr,sizeof(WAVEHDR)); 83 | MMRESULT x2= waveInAddBuffer(hWavIn, WavHdr, sizeof(WAVEHDR)); 84 | } 85 | MMRESULT x3=waveInStart(hWavIn); 86 | MMTIME MMTime = {0}; 87 | //Do nothing...till user gets tired of us and kills the app. 88 | while(1) 89 | { 90 | Sleep(100); 91 | } 92 | waveInReset(hWavIn); 93 | } 94 | -------------------------------------------------------------------------------- /demod_poc24.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_poc24.c -- 2400 baud POCSAG demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * POCSAG (Post Office Code Standard Advisory Group) 8 | * Radio Paging Decoder 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 | */ 24 | 25 | /* ---------------------------------------------------------------------- */ 26 | 27 | #include "multimon.h" 28 | #include "filter.h" 29 | #include 30 | #include 31 | /* ---------------------------------------------------------------------- */ 32 | 33 | #define FREQ_SAMP 22050 34 | #define BAUD 2400 35 | #define FILTLEN 1 36 | 37 | /* ---------------------------------------------------------------------- */ 38 | 39 | #define SPHASEINC (0x10000u*BAUD/FREQ_SAMP) 40 | 41 | /* ---------------------------------------------------------------------- */ 42 | 43 | static void poc24_init(struct demod_state *s) 44 | { 45 | pocsag_init(s); 46 | memset(&s->l1.poc24, 0, sizeof(s->l1.poc24)); 47 | } 48 | 49 | /* ---------------------------------------------------------------------- */ 50 | 51 | static void poc24_demod(struct demod_state *s, buffer_t buffer, int length) 52 | { 53 | for (; length > 0; length--, buffer.fbuffer++) { 54 | s->l1.poc24.dcd_shreg <<= 1; 55 | s->l1.poc24.dcd_shreg |= ((*buffer.fbuffer) > 0); 56 | verbprintf(10, "%c", '0'+(s->l1.poc24.dcd_shreg & 1)); 57 | /* 58 | * check if transition 59 | */ 60 | if ((s->l1.poc24.dcd_shreg ^ (s->l1.poc24.dcd_shreg >> 1)) & 1) { 61 | if (s->l1.poc24.sphase < (0x8000u-(SPHASEINC/2))) 62 | s->l1.poc24.sphase += SPHASEINC/8; 63 | else 64 | s->l1.poc24.sphase -= SPHASEINC/8; 65 | } 66 | s->l1.poc24.sphase += SPHASEINC; 67 | if (s->l1.poc24.sphase >= 0x10000u) { 68 | s->l1.poc24.sphase &= 0xffffu; 69 | pocsag_rxbit(s, s->l1.poc24.dcd_shreg & 1); 70 | } 71 | } 72 | } 73 | 74 | static void poc24_deinit(struct demod_state *s) 75 | { 76 | pocsag_deinit(s); 77 | } 78 | 79 | /* ---------------------------------------------------------------------- */ 80 | 81 | const struct demod_param demod_poc24 = { 82 | "POCSAG2400", true, FREQ_SAMP, FILTLEN, poc24_init, poc24_demod, poc24_deinit 83 | }; 84 | 85 | /* ---------------------------------------------------------------------- */ 86 | -------------------------------------------------------------------------------- /multimon-ng.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= qt 4 | CONFIG -= app_bundle 5 | DEFINES += MAX_VERBOSE_LEVEL=3 6 | QMAKE_CFLAGS += -std=gnu11 7 | QMAKE_CFLAGS += -g # For profiling 8 | 9 | isEmpty(PREFIX) { 10 | PREFIX = /usr/local/ 11 | } 12 | TARGET = multimon-ng 13 | target.path = $$PREFIX/bin 14 | INSTALLS += target 15 | 16 | HEADERS += \ 17 | multimon.h \ 18 | gen.h \ 19 | filter.h \ 20 | filter-i386.h \ 21 | cJSON.h 22 | 23 | SOURCES += \ 24 | unixinput.c \ 25 | uart.c \ 26 | pocsag.c \ 27 | selcall.c \ 28 | hdlc.c \ 29 | demod_zvei1.c \ 30 | demod_zvei2.c \ 31 | demod_zvei3.c \ 32 | demod_pzvei.c \ 33 | demod_dzvei.c \ 34 | demod_ccir.c \ 35 | demod_eia.c \ 36 | demod_eea.c \ 37 | demod_ufsk12.c \ 38 | demod_poc24.c \ 39 | demod_poc12.c \ 40 | demod_poc5.c \ 41 | demod_hapn48.c \ 42 | demod_fsk96.c \ 43 | demod_dtmf.c \ 44 | demod_clipfsk.c \ 45 | demod_fmsfsk.c \ 46 | demod_afsk24.c \ 47 | demod_afsk24_3.c \ 48 | demod_afsk24_2.c \ 49 | demod_afsk12.c \ 50 | demod_flex.c \ 51 | demod_flex_next.c \ 52 | BCHCode.c \ 53 | costabi.c \ 54 | costabf.c \ 55 | clip.c \ 56 | fms.c \ 57 | demod_eas.c \ 58 | demod_morse.c \ 59 | demod_dumpcsv.c \ 60 | demod_x10.c \ 61 | cJSON.c 62 | 63 | macx{ 64 | DEFINES += DUMMY_AUDIO 65 | DEFINES += NO_X11 66 | DEFINES += CHARSET_UTF8 67 | #DEFINES += ARCH_X86_64 68 | #LIBS += -lX11 -L/usr/X11R6/lib -R/usr/X11R6/lib # If you care you can also compile this on OSX. Though 69 | # since Apple will remove Xorg from Mountain Lion I feel 70 | # like we should get rid of this dependency. 71 | } 72 | 73 | win32{ 74 | #DEFINES += DUMMY_AUDIO 75 | DEFINES += WIN32_AUDIO 76 | DEFINES += NO_X11 77 | DEFINES += ONLY_RAW 78 | DEFINES += WINDOWS 79 | SOURCES += win32_soundin.c 80 | LIBS += -lwinmm 81 | #DEFINES += ARCH_I386 82 | } 83 | 84 | unix:freebsd-g++:!symbian:!macx{ 85 | #DEFINES += ARCH_I386 86 | DEFINES += PULSE_AUDIO 87 | DEFINES += CHARSET_UTF8 88 | LIBS += -L/usr/local/lib -lX11 -lpulse-simple -lpulse 89 | SOURCES += xdisplay.c \ 90 | demod_display.c 91 | } 92 | 93 | unix:linux-g++-32:!symbian:!macx{ 94 | #DEFINES += ARCH_I386 95 | DEFINES += PULSE_AUDIO 96 | DEFINES += CHARSET_UTF8 97 | LIBS += -lX11 -lpulse-simple -lpulse 98 | SOURCES += xdisplay.c \ 99 | demod_display.c 100 | } 101 | 102 | unix:linux-g++-64:!symbian:!macx{ 103 | #DEFINES += ARCH_X86_64 104 | DEFINES += PULSE_AUDIO 105 | DEFINES += CHARSET_UTF8 106 | LIBS += -lX11 -lpulse-simple -lpulse 107 | SOURCES += xdisplay.c \ 108 | demod_display.c 109 | } 110 | 111 | unix:linux-g++:!symbian:!macx{ 112 | DEFINES += PULSE_AUDIO 113 | DEFINES += CHARSET_UTF8 114 | LIBS += -lX11 -lpulse-simple -lpulse 115 | SOURCES += xdisplay.c \ 116 | demod_display.c 117 | } 118 | -------------------------------------------------------------------------------- /demod_display.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_display.c -- signal display 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef NO_X11 23 | /* ---------------------------------------------------------------------- */ 24 | 25 | #include "multimon.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /* ---------------------------------------------------------------------- */ 37 | 38 | #define SAMPLING_RATE 22050 39 | 40 | /* ---------------------------------------------------------------------- */ 41 | 42 | static void scope_init(struct demod_state *s) 43 | { 44 | memset(&s->l1.scope, 0, sizeof(s->l1.scope)); 45 | s->l1.scope.dispnum = xdisp_start(); 46 | if (s->l1.scope.dispnum == -1) 47 | return; 48 | } 49 | 50 | /* ---------------------------------------------------------------------- */ 51 | 52 | #define MEMSIZE sizeof(s->l1.scope.data)/sizeof(s->l1.scope.data[0]) 53 | 54 | static void scope_demod(struct demod_state *s, buffer_t buffer, int length) 55 | { 56 | const float *src; 57 | float *dst; 58 | int i; 59 | 60 | if (s->l1.scope.dispnum == -1) 61 | return; 62 | if ( (unsigned int) length >= MEMSIZE) { 63 | src = buffer.fbuffer+length-MEMSIZE; 64 | dst = s->l1.scope.data; 65 | i = MEMSIZE; 66 | } else { 67 | i = MEMSIZE-length; 68 | memmove(s->l1.scope.data, s->l1.scope.data+i, 69 | i*sizeof(s->l1.scope.data[0])); 70 | src = buffer.fbuffer; 71 | dst = s->l1.scope.data+i; 72 | i = length; 73 | } 74 | s->l1.scope.datalen += i; 75 | memcpy(dst, src, i*sizeof(s->l1.scope.data[0])); 76 | if ( (unsigned int) s->l1.scope.datalen < MEMSIZE) 77 | return; 78 | if (xdisp_update(s->l1.scope.dispnum, s->l1.scope.data)) 79 | s->l1.scope.datalen = 0; 80 | } 81 | 82 | /* ---------------------------------------------------------------------- */ 83 | 84 | const struct demod_param demod_scope = { 85 | "SCOPE", true, SAMPLING_RATE, 0, scope_init, scope_demod, NULL 86 | }; 87 | 88 | 89 | /* ---------------------------------------------------------------------- */ 90 | #endif //NO_X11 91 | -------------------------------------------------------------------------------- /gen_zvei.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gen_zvei.c -- generate DTMF sequences 3 | * 4 | * Copyright (C) 1997 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "gen.h" 25 | #include 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | #define PHINC(x) ((float)(x)*0x10000/SAMPLE_RATE) 32 | 33 | static const unsigned int zvei_freq[16] = { 34 | PHINC(2400), PHINC(1060), PHINC(1160), PHINC(1270), 35 | PHINC(1400), PHINC(1530), PHINC(1670), PHINC(1830), 36 | PHINC(2000), PHINC(2200), PHINC(2800), PHINC(810), 37 | PHINC(970), PHINC(886), PHINC(2600), PHINC(0) 38 | }; 39 | 40 | static const unsigned int zveis_freq[16] = { 41 | PHINC(2400), PHINC(1060), PHINC(1160), PHINC(1270), 42 | PHINC(1400), PHINC(1530), PHINC(1670), PHINC(1830), 43 | PHINC(2000), PHINC(2200), PHINC(886), PHINC(810), 44 | PHINC(740), PHINC(680), PHINC(970), PHINC(0) 45 | }; 46 | 47 | void gen_init_zvei(struct gen_params *p, struct gen_state *s) 48 | { 49 | memset(s, 0, sizeof(struct gen_state)); 50 | } 51 | 52 | int gen_zvei(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s) 53 | { 54 | char c; 55 | int num = 0, i; 56 | 57 | for (; buflen > 0; buflen--, buf++, num++) { 58 | if (s->s.zvei.time <= 0) { 59 | c = p->p.zvei.str[s->s.zvei.ch_idx]; 60 | if (!c) 61 | return num; 62 | s->s.zvei.ch_idx++; 63 | if (!isxdigit(c)) { 64 | s->s.zvei.time = s->s.zvei.time2 = 1; 65 | fprintf(stderr, "gen: zvei; invalid char '%c'\n", c); 66 | } else { 67 | s->s.zvei.time = p->p.zvei.duration + p->p.zvei.pause; 68 | s->s.zvei.time2 = p->p.zvei.duration; 69 | if (c >= '0' && c <= '9') 70 | i = c - '0'; 71 | else if (c >= 'A' && c <= 'F') 72 | i = c - 'A' + 10; 73 | else 74 | i = c - 'a' + 10; 75 | s->s.zvei.phinc = zvei_freq[i & 0xf]; 76 | } 77 | } else if (!s->s.zvei.time2) { 78 | s->s.zvei.phinc = 0; 79 | s->s.zvei.ph = 0xc000; 80 | } 81 | s->s.zvei.time--; 82 | s->s.zvei.time2--; 83 | *buf += (p->ampl * COS(s->s.zvei.ph)) >> 15; 84 | s->s.zvei.ph += s->s.zvei.phinc; 85 | } 86 | return num; 87 | } 88 | -------------------------------------------------------------------------------- /gen_dtmf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gen_dtmf.c -- generate DTMF sequences 3 | * 4 | * Copyright (C) 1997 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "gen.h" 25 | #include 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | /* 32 | * 33 | * DTMF frequencies 34 | * 35 | * 1209 1336 1477 1633 36 | * 697 1 2 3 A 37 | * 770 4 5 6 B 38 | * 852 7 8 9 C 39 | * 941 * 0 # D 40 | * 41 | */ 42 | 43 | 44 | static const char *dtmf_transl = "123A456B789C*0#D"; 45 | 46 | #define PHINC(x) ((float)(x)*0x10000/SAMPLE_RATE) 47 | 48 | static const unsigned int row_freq[4] = { 49 | PHINC(697), PHINC(770), PHINC(852), PHINC(941) 50 | }; 51 | 52 | static const unsigned int col_freq[4] = { 53 | PHINC(1209), PHINC(1336), PHINC(1477), PHINC(1633) 54 | }; 55 | 56 | void gen_init_dtmf(struct gen_params *p, struct gen_state *s) 57 | { 58 | memset(s, 0, sizeof(struct gen_state)); 59 | } 60 | 61 | int gen_dtmf(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s) 62 | { 63 | char c; 64 | char *cp; 65 | int num = 0, i; 66 | 67 | for (; buflen > 0; buflen--, buf++, num++) { 68 | if (s->s.dtmf.time <= 0) { 69 | c = p->p.dtmf.str[s->s.dtmf.ch_idx]; 70 | if (!c) 71 | return num; 72 | s->s.dtmf.ch_idx++; 73 | cp = (char*)memchr(dtmf_transl, toupper(c), 16); 74 | if (!cp) { 75 | s->s.dtmf.time = s->s.dtmf.time2 = 1; 76 | fprintf(stderr, "gen: dtmf; invalid char '%c'\n", c); 77 | } else { 78 | s->s.dtmf.time = p->p.dtmf.duration + p->p.dtmf.pause; 79 | s->s.dtmf.time2 = p->p.dtmf.duration; 80 | i = cp - dtmf_transl; 81 | s->s.dtmf.phinc_row = row_freq[(i >> 2) & 3]; 82 | s->s.dtmf.phinc_col = col_freq[i & 3]; 83 | } 84 | } else if (!s->s.dtmf.time2) { 85 | s->s.dtmf.phinc_row = s->s.dtmf.phinc_col = 0; 86 | s->s.dtmf.ph_row = s->s.dtmf.ph_col = 0xc000; 87 | } 88 | s->s.dtmf.time--; 89 | s->s.dtmf.time2--; 90 | *buf += ((p->ampl >> 1) * (COS(s->s.dtmf.ph_row) + COS(s->s.dtmf.ph_col))) >> 15; 91 | s->s.dtmf.ph_row += s->s.dtmf.phinc_row; 92 | s->s.dtmf.ph_col += s->s.dtmf.phinc_col; 93 | } 94 | return num; 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multimon-ng 2 | 3 | multimon-ng is the successor of multimon. It decodes the following digital transmission modes: 4 | 5 | - POCSAG512 POCSAG1200 POCSAG2400 6 | - FLEX 7 | - EAS 8 | - UFSK1200 CLIPFSK AFSK1200 AFSK2400 AFSK2400_2 AFSK2400_3 9 | - HAPN4800 10 | - FSK9600 11 | - DTMF 12 | - ZVEI1 ZVEI2 ZVEI3 DZVEI PZVEI 13 | - EEA EIA CCIR 14 | - MORSE CW 15 | - X10 16 | 17 | ## Building 18 | 19 | multimon-ng can be built using either qmake or CMake: 20 | 21 | #### qmake 22 | ``` 23 | mkdir build 24 | cd build 25 | qmake ../multimon-ng.pro 26 | make 27 | sudo make install 28 | ``` 29 | 30 | #### CMake 31 | ``` 32 | mkdir build 33 | cd build 34 | cmake .. 35 | make 36 | sudo make install 37 | ``` 38 | 39 | The installation prefix can be set by passing a 'PREFIX' parameter to qmake. e.g: 40 | ```qmake multimon-ng.pro PREFIX=/usr/local``` 41 | 42 | ### Environments 43 | 44 | So far multimon-ng has been successfully built on: 45 | 46 | - Arch Linux 47 | - Debian 48 | - Gentoo 49 | - Kali Linux 50 | - Ubuntu 51 | - OS X 52 | - Windows (Qt-MinGW build environment, Cygwin, and VisualStudio/MSVC) 53 | - FreeBSD 54 | 55 | ## Examples 56 | 57 | ### Wav to raw 58 | 59 | Files can be easily converted into multimon-ng's native raw format using *sox*. e.g: 60 | 61 | sox -t wav pocsag_short.wav -esigned-integer -b16 -r 22050 -t raw pocsag_short.raw 62 | 63 | GNURadio can also generate the format using the file sink in input mode *short*. 64 | 65 | ### Pipe sox to multimon-ng 66 | 67 | You can also "pipe" raw samples into multimon-ng using something like: 68 | 69 | sox -t wav pocsag_short.wav -esigned-integer -b16 -r 22050 -t raw - | ./multimon-ng - 70 | 71 | > [!NOTE] 72 | > Note the trailing dash, means write/read to/from stdin 73 | 74 | ### Pipe rtl_fm to multimon-ng 75 | 76 | As a last example, here is how you can use it in combination with RTL-SDR: 77 | 78 | rtl_fm -f 403600000 -s 22050 | multimon-ng -t raw -a FMSFSK -a AFSK1200 /dev/stdin 79 | 80 | ### Flac record and parse live data 81 | 82 | A more advanced sample that combines `rtl_fm`, `flac`, and `tee` to split the output from `rtl_rm` into separate streams. One stream to be passed to `flac` to record the audio and another stream to for example an application that does text parsing of `mulimon-ng` output 83 | 84 | 85 | 86 | ```sh 87 | rtl_fm -s 22050 -f 123.456M -g -9.9 | tee >(flac -8 --endian=little --channels=1 --bps=16 --sample-rate=22050 --sign=signed - -o ~/recordings/rtlfm.$EPOCHSECONDS.flac -f) | multimon-ng -v 0 -a FLEX -a FLEX_NEXT -t raw /dev/stdin 88 | ``` 89 | 90 | 1. You can pass `-l` to `rtl_fm` for the squelch level, this will cut the noise floor so less data gets encoded by flac and will significantly reduce the file size but could result in loss of signal data. **This value must be tuned!** 91 | 2. Flac uses `-8` here, if you run an a resource constraint device you may want to lower this value 92 | 3. The Flac `-o` argument value contains `$EPOCHSECONDS` to make unique files when this gets restarted 93 | 94 | To replay the recorded flac file to multimon-ng (requires sox): 95 | 96 | ```sg 97 | flac -d --stdout ~/recordings/rtlf/rtlfm.1725033204.flac | multimon-ng -v 0 -a FLEX_NEXT -t flac - 98 | ``` 99 | 100 | ## Packaging 101 | 102 | ``` 103 | qmake multimon-ng.pro PREFIX=/usr/local 104 | make 105 | make install INSTALL_ROOT=/ 106 | ``` 107 | -------------------------------------------------------------------------------- /demod_poc5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_poc5.c -- 512 baud POCSAG demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * Copyright (C) 2024 7 | * Marat Fayzullin (luarvique@gmail.com) 8 | * 9 | * POCSAG (Post Office Code Standard Advisory Group) 10 | * Radio Paging Decoder 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program; if not, write to the Free Software 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 | */ 26 | 27 | /* ---------------------------------------------------------------------- */ 28 | 29 | #include "multimon.h" 30 | #include "filter.h" 31 | #include 32 | #include 33 | /* ---------------------------------------------------------------------- */ 34 | 35 | #define FREQ_SAMP 22050 36 | #define BAUD 512 37 | #define SUBSAMP 5 38 | #define FILTLEN 1 39 | 40 | /* ---------------------------------------------------------------------- */ 41 | 42 | #define SPHASEINC (0x10000u*BAUD*SUBSAMP/FREQ_SAMP) 43 | 44 | /* ---------------------------------------------------------------------- */ 45 | 46 | static void poc5_init(struct demod_state *s) 47 | { 48 | pocsag_init(s); 49 | memset(&s->l1.poc5, 0, sizeof(s->l1.poc5)); 50 | } 51 | 52 | /* ---------------------------------------------------------------------- */ 53 | 54 | static void poc5_demod(struct demod_state *s, buffer_t buffer, int length) 55 | { 56 | if (s->l1.poc5.subsamp) { 57 | if (length <= (int)s->l1.poc5.subsamp) { 58 | s->l1.poc5.subsamp -= length; 59 | return; 60 | } 61 | buffer.fbuffer += s->l1.poc5.subsamp; 62 | length -= s->l1.poc5.subsamp; 63 | s->l1.poc5.subsamp = 0; 64 | } 65 | for (; length > 0; length -= SUBSAMP, buffer.fbuffer += SUBSAMP) { 66 | s->l1.poc5.dcd_shreg <<= 1; 67 | s->l1.poc5.dcd_shreg |= ((*buffer.fbuffer) > 0); 68 | verbprintf(10, "%c", '0'+(s->l1.poc5.dcd_shreg & 1)); 69 | /* 70 | * check if transition 71 | */ 72 | if ((s->l1.poc5.dcd_shreg ^ (s->l1.poc5.dcd_shreg >> 1)) & 1) { 73 | if (s->l1.poc5.sphase < (0x8000u-(SPHASEINC/2))) 74 | s->l1.poc5.sphase += SPHASEINC/8; 75 | else 76 | s->l1.poc5.sphase -= SPHASEINC/8; 77 | } 78 | s->l1.poc5.sphase += SPHASEINC; 79 | if (s->l1.poc5.sphase >= 0x10000u) { 80 | s->l1.poc5.sphase &= 0xffffu; 81 | pocsag_rxbit(s, s->l1.poc5.dcd_shreg & 1); 82 | } 83 | } 84 | s->l1.poc5.subsamp = -length; 85 | } 86 | 87 | static void poc5_deinit(struct demod_state *s) 88 | { 89 | pocsag_deinit(s); 90 | } 91 | 92 | /* ---------------------------------------------------------------------- */ 93 | 94 | const struct demod_param demod_poc5 = { 95 | "POCSAG512", true, FREQ_SAMP, FILTLEN, poc5_init, poc5_demod, poc5_deinit 96 | }; 97 | 98 | /* ---------------------------------------------------------------------- */ 99 | -------------------------------------------------------------------------------- /unsupported/Makefile.legacy: -------------------------------------------------------------------------------- 1 | DEBUG =n 2 | OS =$(shell uname) 3 | MACH =$(shell uname -m) 4 | 5 | CFLAGS =-Wall -Wstrict-prototypes -I/usr/X11R6/include 6 | 7 | ifeq ($(OS),Darwin) 8 | CFLAGS +=-DDUMMY_AUDIO 9 | CFLAGS +=-DNO_X11 10 | endif 11 | 12 | ifeq ($(OS),CYGWIN_NT-6.1) 13 | CFLAGS +=-DDUMMY_AUDIO 14 | CFLAGS +=-DNO_X11 15 | endif 16 | 17 | ifeq ($(OS),SunOS) 18 | ifeq ($(DEBUG),y) 19 | CFLAGS +=-g -O -DSUN_AUDIO -DARCH_SPARC 20 | else 21 | CFLAGS +=-O3 -DSUN_AUDIO -DARCH_SPARC 22 | endif 23 | LDFLAGSX =-lX11 -L/usr/X11R6/lib -R/usr/X11R6/lib -lsocket -lnsl 24 | else 25 | ifeq ($(MACH),x86_64) 26 | ifeq ($(DEBUG),y) 27 | CFLAGS +=-g -O -DARCH_X86_64 -DPULSE_AUDIO 28 | else 29 | CFLAGS +=-O3 -DARCH_X86_64 -DPULSE_AUDIO 30 | endif 31 | LDFLAGSX = -lX11 -L/usr/X11R6/lib -lpulse-simple 32 | else 33 | ifeq ($(DEBUG),y) 34 | CFLAGS +=-g -O -falign-loops=2 -falign-jumps=2 \ 35 | -malign-functions=2 -DPULSE_AUDIO #-DARCH_I386 -march=i486 36 | else 37 | CFLAGS +=-O3 -falign-loops=2 -falign-jumps=2 \ 38 | -falign-functions=2 -DPULSE_AUDIO #-DARCH_I386 -march=i486 39 | endif 40 | LDFLAGSX = -lX11 -L/usr/X11R6/lib -lpulse-simple 41 | endif 42 | endif 43 | 44 | 45 | BINDIR =bin-$(shell uname -m) 46 | 47 | AS86 =as86 -0 -a 48 | LD86 =ld86 -0 49 | 50 | AS =as 51 | LD =ld 52 | LDFLAGS =-lm 53 | HOSTCC =gcc 54 | CC =gcc 55 | MAKE =make 56 | CPP =$(CC) -E 57 | AR =ar 58 | STRIP =strip 59 | MKDIR =mkdir 60 | 61 | all: $(BINDIR) $(BINDIR)/multimon #$(BINDIR)/gen 62 | 63 | $(BINDIR)/%.s: %.c 64 | $(CC) $(CFLAGS) -S -o $@ $< 65 | 66 | $(BINDIR)/%.o: $(BINDIR)/%.s 67 | $(AS) -c -o $@ $< 68 | 69 | $(BINDIR)/%.o: %.c 70 | $(CC) $(CFLAGS) -c -o $@ $< 71 | 72 | SRC_L2 =hdlc.c pocsag.c uart.c clip.c 73 | SRC_L1 =demod_afsk12.c demod_afsk24.c demod_afsk24_2.c demod_afsk24_3.c 74 | SRC_L1 +=demod_ufsk12.c demod_clipfsk.c 75 | SRC_L1 +=demod_hapn48.c demod_fsk96.c 76 | SRC_L1 +=demod_poc5.c demod_poc12.c demod_poc24.c 77 | SRC_L1 +=demod_dtmf.c demod_zvei.c demod_eas.c demod_display.c 78 | SRC_MISC =unixinput.c costabf.c xdisplay.c 79 | 80 | SRC_GEN =gen.c gen_dtmf.c gen_sin.c gen_zvei.c gen_hdlc.c gen_uart.c gen_clipfsk.c costabi.c 81 | 82 | OBJ_L2 =$(SRC_L2:%.c=$(BINDIR)/%.o) 83 | OBJ_L1 =$(SRC_L1:%.c=$(BINDIR)/%.o) 84 | OBJ_MISC =$(SRC_MISC:%.c=$(BINDIR)/%.o) 85 | 86 | OBJ_GEN =$(SRC_GEN:%.c=$(BINDIR)/%.o) 87 | 88 | $(BINDIR): 89 | $(MKDIR) $(BINDIR) 90 | 91 | $(BINDIR)/multimon: $(OBJ_L2) $(OBJ_L1) $(OBJ_MISC) 92 | $(CC) $^ $(LDFLAGS) $(LDFLAGSX) -o $@ 93 | 94 | #$(BINDIR)/gen: $(OBJ_GEN) 95 | # $(CC) $^ $(LDFLAGS) -o $@ 96 | 97 | #$(BINDIR)/mkcostab: $(BINDIR)/mkcostab.o 98 | # $(CC) $^ $(LDFLAGS) -o $@ 99 | 100 | #costabi.c costabf.c: $(BINDIR)/mkcostab 101 | # $(BINDIR)/mkcostab 102 | 103 | clean: 104 | $(RM) -f core `find . -name '*.[oas]' -print` 105 | $(RM) -f core `find . -name 'core' -print` 106 | $(RM) -f core costabi.c costabf.c *~ multimon.tar.bz2 107 | $(RM) -rf bin-* 108 | 109 | 110 | depend dep: $(BINDIR) costabi.c costabf.c 111 | $(CPP) -M $(CFLAGS) $(SRC_MISC) $(SRC_L1) $(SRC_L2) $(SRC_GEN) mkcostab.c > $(BINDIR)/.depend 112 | 113 | dist: 114 | tar cjf multimon.tar.bz2 COPYING Makefile filter.h filter-i386.h gen.h multimon.h \ 115 | $(SRC_MISC) $(SRC_L1) $(SRC_L2) $(SRC_GEN) mkcostab.c 116 | 117 | ifeq ($(BINDIR)/.depend,$(wildcard $(BINDIR)/.depend)) 118 | include $(BINDIR)/.depend 119 | endif 120 | -------------------------------------------------------------------------------- /demod_poc12.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_poc12.c -- 1200 baud POCSAG demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * Copyright (C) 2024 7 | * Marat Fayzullin (luarvique@gmail.com) 8 | * 9 | * POCSAG (Post Office Code Standard Advisory Group) 10 | * Radio Paging Decoder 11 | * 12 | * This program is free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This program is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with this program; if not, write to the Free Software 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 | */ 26 | 27 | /* ---------------------------------------------------------------------- */ 28 | 29 | #include "multimon.h" 30 | #include "filter.h" 31 | #include 32 | #include 33 | #include 34 | 35 | /* ---------------------------------------------------------------------- */ 36 | 37 | #define FREQ_SAMP 22050 38 | #define BAUD 1200 39 | #define SUBSAMP 2 40 | #define FILTLEN 1 41 | 42 | /* ---------------------------------------------------------------------- */ 43 | 44 | #define SPHASEINC (0x10000u*BAUD*SUBSAMP/FREQ_SAMP) 45 | 46 | /* ---------------------------------------------------------------------- */ 47 | 48 | static void poc12_init(struct demod_state *s) 49 | { 50 | pocsag_init(s); 51 | memset(&s->l1.poc12, 0, sizeof(s->l1.poc12)); 52 | } 53 | 54 | /* ---------------------------------------------------------------------- */ 55 | 56 | static void poc12_demod(struct demod_state *s, buffer_t buffer, int length) 57 | { 58 | if (s->l1.poc12.subsamp) { 59 | if (length <= (int)s->l1.poc12.subsamp) { 60 | s->l1.poc12.subsamp -= length; 61 | return; 62 | } 63 | buffer.fbuffer += s->l1.poc12.subsamp; 64 | length -= s->l1.poc12.subsamp; 65 | s->l1.poc12.subsamp = 0; 66 | } 67 | for (; length > 0; length -= SUBSAMP, buffer.fbuffer += SUBSAMP) { 68 | s->l1.poc12.dcd_shreg <<= 1; 69 | s->l1.poc12.dcd_shreg |= ((*buffer.fbuffer) > 0); 70 | verbprintf(10, "%c", '0'+(s->l1.poc12.dcd_shreg & 1)); 71 | /* 72 | * check if transition 73 | */ 74 | if ((s->l1.poc12.dcd_shreg ^ (s->l1.poc12.dcd_shreg >> 1)) & 1) { 75 | if (s->l1.poc12.sphase < (0x8000u-(SPHASEINC/2))) 76 | s->l1.poc12.sphase += SPHASEINC/8; 77 | else 78 | s->l1.poc12.sphase -= SPHASEINC/8; 79 | } 80 | s->l1.poc12.sphase += SPHASEINC; 81 | if (s->l1.poc12.sphase >= 0x10000u) { 82 | s->l1.poc12.sphase &= 0xffffu; 83 | pocsag_rxbit(s, s->l1.poc12.dcd_shreg & 1); 84 | } 85 | } 86 | s->l1.poc12.subsamp = -length; 87 | } 88 | 89 | static void poc12_deinit(struct demod_state *s) 90 | { 91 | pocsag_deinit(s); 92 | } 93 | 94 | /* ---------------------------------------------------------------------- */ 95 | 96 | const struct demod_param demod_poc12 = { 97 | "POCSAG1200", true, FREQ_SAMP, FILTLEN, poc12_init, poc12_demod, poc12_deinit 98 | }; 99 | 100 | /* ---------------------------------------------------------------------- */ 101 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 2.8.12...3.10 ) 2 | 3 | # The "MSVC" flag isn't set until the "project" command 4 | # is called. Let's just check the operating system. 5 | if( NOT WIN32 ) 6 | project( multimon-ng C ) 7 | else( NOT WIN32 ) 8 | # Visual Studio C compiler doesn't support C99 (i.e. stdbool.h); 9 | # so use the Visual Studio C++ compiler instead 10 | project( multimon-ng CXX ) 11 | endif( NOT WIN32 ) 12 | 13 | set( TARGET "${PROJECT_NAME}" ) 14 | set( VERSION "1.4.1" ) 15 | set( MAJOR "1" ) 16 | set( MINOR "4" ) 17 | set( PATCH "1" ) 18 | set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra" ) 19 | 20 | if( WIN32 ) 21 | add_definitions( "-DWIN32_AUDIO" "-DONLY_RAW" "-DWINDOWS" ) 22 | link_libraries( ${TARGET} "-lwinmm" ) 23 | set( SOURCES ${SOURCES} 24 | "win32_soundin.c" 25 | ) 26 | elseif( UNIX ) 27 | find_package( X11 ) 28 | if ( X11_FOUND ) 29 | option( X11_SUPPORT "Enable X11 display support" ${X11_FOUND} ) 30 | mark_as_advanced( X11_SUPPORT ) 31 | endif( X11_FOUND ) 32 | find_package( PulseAudio ) 33 | if ( PULSEAUDIO_FOUND ) 34 | option( PULSE_AUDIO_SUPPORT "Enable pulse audio support" ${PULSEAUDIO_FOUND} ) 35 | mark_as_advanced( PULSE_AUDIO_SUPPORT ) 36 | endif( PULSEAUDIO_FOUND ) 37 | 38 | # Check if we can use the GCC/llvm __builtin_popcount 39 | include( CheckCSourceCompiles ) 40 | check_c_source_compiles( 41 | "int main() { __builtin_popcount(42); return 0; }" USE_BUILTIN_POPCOUNT ) 42 | 43 | set( INSTALL_MAN_DIR "${CMAKE_INSTALL_PREFIX}/share/man" ) 44 | install( FILES multimon-ng.1 DESTINATION "${INSTALL_MAN_DIR}/man1" ) 45 | endif( WIN32 ) 46 | 47 | if( X11_SUPPORT ) 48 | include_directories( ${X11_INCLUDE_DIR} ) 49 | link_libraries( ${X11_LIBRARIES} ) 50 | set( SOURCES ${SOURCES} 51 | xdisplay.c 52 | demod_display.c 53 | ) 54 | else( X11_SUPPORT ) 55 | add_definitions( "-DNO_X11" ) 56 | endif( X11_SUPPORT ) 57 | 58 | if( PULSE_AUDIO_SUPPORT ) 59 | include_directories( ${PULSEAUDIO_INCLUDE_DIR} ) 60 | find_library( PULSE_SIMPLE NAMES pulse-simple REQUIRED ) 61 | link_libraries( ${PULSEAUDIO_LIBRARY} ${PULSE_SIMPLE} ) 62 | add_definitions( "-DPULSE_AUDIO" ) 63 | else( PULSE_AUDIO_SUPPORT ) 64 | add_definitions( "-DDUMMY_AUDIO" ) 65 | endif( PULSE_AUDIO_SUPPORT ) 66 | 67 | if( NOT MSVC ) 68 | add_definitions( "-std=gnu11" ) 69 | endif( NOT MSVC ) 70 | add_definitions( "-DMAX_VERBOSE_LEVEL=3" "-DCHARSET_UTF8" ) 71 | 72 | if ( EXISTS "${multimon-ng_SOURCE_DIR}/BCHCode.c" ) 73 | set( SOURCES ${SOURCES} 74 | BCHCode.c ) 75 | else() 76 | message(STATUS "Using the BCH decoder stub") 77 | set( SOURCES ${SOURCES} 78 | BCHCode_stub.c ) 79 | endif() 80 | 81 | set( HEADERS ${HEADERS} 82 | multimon.h 83 | gen.h 84 | filter.h 85 | filter-i386.h 86 | cJSON.h 87 | ) 88 | 89 | set( SOURCES ${SOURCES} 90 | unixinput.c 91 | uart.c 92 | pocsag.c 93 | selcall.c 94 | hdlc.c 95 | demod_zvei1.c 96 | demod_zvei2.c 97 | demod_zvei3.c 98 | demod_pzvei.c 99 | demod_dzvei.c 100 | demod_ccir.c 101 | demod_eia.c 102 | demod_eea.c 103 | demod_ufsk12.c 104 | demod_poc24.c 105 | demod_poc12.c 106 | demod_poc5.c 107 | demod_hapn48.c 108 | demod_fsk96.c 109 | demod_dtmf.c 110 | demod_clipfsk.c 111 | demod_fmsfsk.c 112 | demod_afsk24.c 113 | demod_afsk24_3.c 114 | demod_afsk24_2.c 115 | demod_afsk12.c 116 | demod_flex.c 117 | demod_flex_next.c 118 | costabi.c 119 | costabf.c 120 | clip.c 121 | fms.c 122 | demod_eas.c 123 | demod_morse.c 124 | demod_dumpcsv.c 125 | demod_x10.c 126 | cJSON.c 127 | ) 128 | 129 | 130 | add_executable( "${TARGET}" ${SOURCES} ${HEADERS} ) 131 | set_property(TARGET "${TARGET}" PROPERTY LINKER_LANGUAGE C) 132 | target_link_libraries( "${TARGET}" m ) 133 | install(TARGETS multimon-ng DESTINATION bin) 134 | 135 | -------------------------------------------------------------------------------- /demod_hapn48.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_hapn48.c -- HAPN 4800 baud demodulator (G3RUH) 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "multimon.h" 25 | #include "filter.h" 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | #define FREQ_SAMP 22050 32 | #define BAUD 4800 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | #define SPHASEINC (0x10000u*BAUD/FREQ_SAMP) 37 | 38 | /* ---------------------------------------------------------------------- */ 39 | 40 | static void hapn48_init(struct demod_state *s) 41 | { 42 | hdlc_init(s); 43 | memset(&s->l1.hapn48, 0, sizeof(s->l1.hapn48)); 44 | } 45 | 46 | /* ---------------------------------------------------------------------- */ 47 | 48 | static void hapn48_demod(struct demod_state *s, buffer_t buffer, int length) 49 | { 50 | unsigned int curbit; 51 | 52 | for (; length > 0; length--, buffer.fbuffer++) { 53 | s->l1.hapn48.lvlhi *= 0.999; 54 | s->l1.hapn48.lvllo *= 0.999; 55 | if (buffer.fbuffer[1] > s->l1.hapn48.lvlhi) 56 | s->l1.hapn48.lvlhi = buffer.fbuffer[1]; 57 | if (buffer.fbuffer[1] < s->l1.hapn48.lvllo) 58 | s->l1.hapn48.lvllo = buffer.fbuffer[1]; 59 | s->l1.hapn48.shreg = (s->l1.hapn48.shreg << 1) | 60 | (s->l1.hapn48.shreg & 1); 61 | if (buffer.fbuffer[1] > s->l1.hapn48.lvlhi * 0.5) { 62 | s->l1.hapn48.shreg |= 1; 63 | } else if (buffer.fbuffer[1] < s->l1.hapn48.lvllo * 0.5) { 64 | s->l1.hapn48.shreg &= ~1; 65 | } 66 | verbprintf(10, "%c", '0' + (s->l1.hapn48.shreg & 1)); 67 | s->l1.hapn48.sphase += SPHASEINC; 68 | if (((s->l1.hapn48.shreg >> 1) ^ s->l1.hapn48.shreg) & 1) { 69 | if (s->l1.hapn48.sphase >= 0x8000+SPHASEINC/2) 70 | s->l1.hapn48.sphase -= 0x800; 71 | else 72 | s->l1.hapn48.sphase += 0x800; 73 | } 74 | #ifdef HAPN48_CURSYNC 75 | int cursync = 0; 76 | if (buffer.fbuffer[1] > s->l1.hapn48.lvlhi * 0.5) { 77 | cursync = (buffer.fbuffer[1] > buffer.fbuffer[0] && buffer.fbuffer[1] > buffer.fbuffer[2]); 78 | } else if (buffer.fbuffer[1] < s->l1.hapn48.lvllo * 0.5) { 79 | cursync = (buffer.fbuffer[1] < buffer.fbuffer[0] && buffer.fbuffer[1] < buffer.fbuffer[2]); 80 | } 81 | if (cursync) { 82 | if (((s->l1.hapn48.sphase-0x8000)&0xffffu) >= 0x8000+SPHASEINC/2) 83 | s->l1.hapn48.sphase -= 0x800; 84 | else 85 | s->l1.hapn48.sphase += 0x800; 86 | } 87 | #endif 88 | if (s->l1.hapn48.sphase >= 0x10000) { 89 | s->l1.hapn48.sphase &= 0xffff; 90 | curbit = ((s->l1.hapn48.shreg >> 4) ^ s->l1.hapn48.shreg ^ 1) & 1; 91 | verbprintf(9, " %c ", '0'+curbit); 92 | hdlc_rxbit(s, curbit); 93 | } 94 | } 95 | } 96 | 97 | /* ---------------------------------------------------------------------- */ 98 | 99 | const struct demod_param demod_hapn4800 = { 100 | "HAPN4800", true, FREQ_SAMP, 3, hapn48_init, hapn48_demod, NULL 101 | }; 102 | 103 | /* ---------------------------------------------------------------------- */ 104 | -------------------------------------------------------------------------------- /gen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gen.h -- generate different test signals 3 | * 4 | * Copyright (C) 1997 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #define SAMPLE_RATE 22050 25 | #define MS(x) ((float)(x)*SAMPLE_RATE/1000) 26 | 27 | extern const int costabi[0x400]; 28 | 29 | #define COS(x) costabi[(((x)>>6)&0x3ffu)] 30 | 31 | enum gen_type { gentype_dtmf, gentype_sine, gentype_zvei, gentype_hdlc, gentype_uart, gentype_clipfsk }; 32 | 33 | struct gen_params { 34 | enum gen_type type; 35 | int ampl; 36 | union { 37 | struct { 38 | int duration; 39 | int pause; 40 | char str[256]; 41 | } dtmf; 42 | struct { 43 | int duration; 44 | int freq; 45 | } sine; 46 | struct { 47 | int duration; 48 | int pause; 49 | char str[256]; 50 | } zvei; 51 | struct { 52 | int modulation; 53 | int txdelay; 54 | int pktlen; 55 | unsigned char pkt[256]; 56 | } uart; 57 | struct { 58 | int modulation; 59 | int txdelay; 60 | int pktlen; 61 | unsigned char pkt[256]; 62 | } clipfsk; 63 | struct { 64 | int modulation; 65 | int txdelay; 66 | int pktlen; 67 | unsigned char pkt[256]; 68 | } hdlc; 69 | } p; 70 | }; 71 | 72 | struct gen_state { 73 | union { 74 | struct { 75 | int ch_idx; 76 | int ph_row, ph_col, phinc_row, phinc_col; 77 | int time, time2; 78 | } dtmf; 79 | struct { 80 | int ph, phinc; 81 | int time; 82 | } sine; 83 | struct { 84 | int ch_idx; 85 | int ph, phinc; 86 | int time, time2; 87 | } zvei; 88 | struct { 89 | int ch_idx, bitmask; 90 | unsigned int ph, phinc, bitph; 91 | unsigned int datalen; 92 | unsigned char data[512]; 93 | } uart; 94 | struct { 95 | int ch_idx, bitmask; 96 | unsigned int ph, phinc, bitph; 97 | unsigned int datalen; 98 | unsigned char data[512]; 99 | } clipfsk; 100 | struct { 101 | int lastb; 102 | int ch_idx, bitmask; 103 | unsigned int ph, phinc, bitph; 104 | unsigned int datalen; 105 | unsigned char data[512]; 106 | } hdlc; 107 | } s; 108 | }; 109 | 110 | extern void gen_init_dtmf(struct gen_params *p, struct gen_state *s); 111 | extern int gen_dtmf(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s); 112 | 113 | extern void gen_init_sine(struct gen_params *p, struct gen_state *s); 114 | extern int gen_sine(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s); 115 | 116 | extern void gen_init_zvei(struct gen_params *p, struct gen_state *s); 117 | extern int gen_zvei(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s); 118 | 119 | extern void gen_init_uart(struct gen_params *p, struct gen_state *s); 120 | extern int gen_uart(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s); 121 | 122 | extern void gen_init_clipfsk(struct gen_params *p, struct gen_state *s); 123 | extern int gen_clipfsk(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s); 124 | 125 | extern void gen_init_hdlc(struct gen_params *p, struct gen_state *s); 126 | extern int gen_hdlc(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s); 127 | 128 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uart.c -- uart decoder and packet dump 3 | * 4 | * Copyright (C) 2007 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "multimon.h" 24 | #include 25 | 26 | /* ---------------------------------------------------------------------- */ 27 | 28 | static void disp_packet(struct demod_state *s, unsigned char *bp, unsigned int len) 29 | { 30 | unsigned char i,j; 31 | (void) s; // Suppress the warning. 32 | 33 | if (!bp) 34 | return; 35 | if (!len) { 36 | verbprintf(0, "\n"); 37 | return; 38 | } 39 | j = 0; 40 | while (len) { 41 | i = *bp++; 42 | if ((i >= 32) && (i < 128)) 43 | verbprintf(0, "%c",i); 44 | else if (i == 13) { 45 | if (j) 46 | verbprintf(0, "\n"); 47 | j = 0; 48 | } else 49 | verbprintf(0, "[0x%02X]",i); 50 | 51 | if (i >= 32) 52 | j = 1; 53 | 54 | len--; 55 | } 56 | if (j) 57 | verbprintf(0, "\n"); 58 | } 59 | 60 | /* ---------------------------------------------------------------------- */ 61 | 62 | void uart_init(struct demod_state *s) 63 | { 64 | memset(&s->l2.uart, 0, sizeof(s->l2.uart)); 65 | s->l2.uart.rxptr = s->l2.uart.rxbuf; 66 | } 67 | 68 | /* ---------------------------------------------------------------------- */ 69 | 70 | void uart_rxbit(struct demod_state *s, int bit) 71 | { 72 | s->l2.uart.rxbitstream <<= 1; 73 | s->l2.uart.rxbitstream |= !!bit; 74 | if (!s->l2.uart.rxstate) { 75 | switch (s->l2.uart.rxbitstream & 0x03) { 76 | case 0x02: /* start bit */ 77 | s->l2.uart.rxstate = 1; 78 | s->l2.uart.rxbitbuf = 0x100; 79 | break; 80 | case 0x00: /* no start bit */ 81 | case 0x03: /* consecutive stop bits*/ 82 | if ((s->l2.uart.rxptr - s->l2.uart.rxbuf) >= 1) 83 | disp_packet(s, s->l2.uart.rxbuf, s->l2.uart.rxptr - s->l2.uart.rxbuf); 84 | s->l2.uart.rxptr = s->l2.uart.rxbuf; 85 | break; 86 | } 87 | return; 88 | } 89 | if (s->l2.uart.rxbitstream & 1) 90 | s->l2.uart.rxbitbuf |= 0x200; 91 | // verbprintf(7, "b=%c", '0'+(s->l2.uart.rxbitstream & 1)); 92 | if (s->l2.uart.rxbitbuf & 1) { 93 | if (s->l2.uart.rxptr >= s->l2.uart.rxbuf+sizeof(s->l2.uart.rxbuf)) { 94 | s->l2.uart.rxstate = 0; 95 | disp_packet(s, s->l2.uart.rxbuf, s->l2.uart.rxptr - s->l2.uart.rxbuf); 96 | verbprintf(1, "Error: packet size too large\n"); 97 | return; 98 | } 99 | if ( !(s->l2.uart.rxbitstream & 1) ) { 100 | s->l2.uart.rxstate = 0; 101 | verbprintf(1, "Error: stop bit is 0. Bad framing\n"); 102 | return; 103 | } 104 | *s->l2.uart.rxptr++ = s->l2.uart.rxbitbuf >> 1; 105 | // verbprintf(6, "B=%02X ", (s->l2.uart.rxbitbuf >> 1) & 0xff); 106 | // verbprintf(5, "%c", (s->l2.uart.rxbitbuf >> 1) & 0xff); 107 | s->l2.uart.rxbitbuf = 0x100; 108 | s->l2.uart.rxstate = 0; 109 | return; 110 | } 111 | s->l2.uart.rxbitbuf >>= 1; 112 | } 113 | 114 | /* ---------------------------------------------------------------------- */ 115 | -------------------------------------------------------------------------------- /filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * filter.h -- optimized filter routines 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #ifndef _FILTER_H 25 | #define _FILTER_H 26 | 27 | /* ---------------------------------------------------------------------- */ 28 | 29 | #ifdef ARCH_I386 30 | #include "filter-i386.h" 31 | #endif /* ARCH_I386 */ 32 | 33 | /* ---------------------------------------------------------------------- */ 34 | 35 | static inline unsigned int hweight32(unsigned int w) 36 | #ifndef _MSC_VER 37 | __attribute__ ((unused)) 38 | #endif 39 | ; 40 | static inline unsigned int hweight16(unsigned short w) 41 | #ifndef _MSC_VER 42 | __attribute__ ((unused)) 43 | #endif 44 | ; 45 | 46 | static inline unsigned int hweight8(unsigned char w) 47 | #ifndef _MSC_VER 48 | __attribute__ ((unused)) 49 | #endif 50 | ; 51 | 52 | static inline unsigned int hweight32(unsigned int w) 53 | { 54 | unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); 55 | res = (res & 0x33333333) + ((res >> 2) & 0x33333333); 56 | res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); 57 | res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); 58 | return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); 59 | } 60 | 61 | static inline unsigned int hweight16(unsigned short w) 62 | { 63 | unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555); 64 | res = (res & 0x3333) + ((res >> 2) & 0x3333); 65 | res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); 66 | return (res & 0x00FF) + ((res >> 8) & 0x00FF); 67 | } 68 | 69 | static inline unsigned int hweight8(unsigned char w) 70 | { 71 | unsigned short res = (w & 0x55) + ((w >> 1) & 0x55); 72 | res = (res & 0x33) + ((res >> 2) & 0x33); 73 | return (res & 0x0F) + ((res >> 4) & 0x0F); 74 | } 75 | 76 | static inline unsigned int gcd(unsigned int x, unsigned int y) 77 | #ifndef _MSC_VER 78 | __attribute__ ((unused)) 79 | #endif 80 | ; 81 | static inline unsigned int lcm(unsigned int x, unsigned int y) 82 | #ifndef _MSC_VER 83 | __attribute__ ((unused)) 84 | #endif 85 | ; 86 | 87 | static inline unsigned int gcd(unsigned int x, unsigned int y) 88 | { 89 | for (;;) { 90 | if (!x) 91 | return y; 92 | if (!y) 93 | return x; 94 | if (x > y) 95 | x %= y; 96 | else 97 | y %= x; 98 | } 99 | } 100 | 101 | static inline unsigned int lcm(unsigned int x, unsigned int y) 102 | { 103 | return x * y / gcd(x, y); 104 | } 105 | 106 | /* ---------------------------------------------------------------------- */ 107 | 108 | #ifndef __HAVE_ARCH_MAC 109 | static inline float mac(const float *a, const float *b, unsigned int size) 110 | { 111 | float sum = 0; 112 | unsigned int i; 113 | 114 | for (i = 0; i < size; i++) 115 | sum += (*a++) * (*b++); 116 | return sum; 117 | } 118 | #endif /* __HAVE_ARCH_MAC */ 119 | 120 | static inline float fsqr(float f) 121 | { 122 | return f*f; 123 | } 124 | 125 | /* ---------------------------------------------------------------------- */ 126 | #endif /* _FILTER_H */ 127 | -------------------------------------------------------------------------------- /demod_ufsk12.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_ufsk12.c -- 1200 baud FSK demodulator 3 | * 4 | * Copyright (C) 2007, 2024 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "multimon.h" 24 | #include "filter.h" 25 | #include 26 | #include 27 | 28 | /* ---------------------------------------------------------------------- */ 29 | 30 | /* 31 | * Standard TCM3105 clock frequency: 4.4336MHz 32 | * Mark frequency: 2200 Hz 33 | * Space frequency: 1200 Hz 34 | */ 35 | 36 | #define FREQ_MARK 1200 37 | #define FREQ_SPACE 2200 38 | #define FREQ_SAMP 22050 39 | #define BAUD 1200 40 | #define SUBSAMP 2 41 | 42 | /* ---------------------------------------------------------------------- */ 43 | 44 | #define CORRLEN ((int)(FREQ_SAMP/BAUD)) 45 | #define SPHASEINC (0x10000u*BAUD*SUBSAMP/FREQ_SAMP) 46 | 47 | static float corr_mark_i[CORRLEN]; 48 | static float corr_mark_q[CORRLEN]; 49 | static float corr_space_i[CORRLEN]; 50 | static float corr_space_q[CORRLEN]; 51 | 52 | /* ---------------------------------------------------------------------- */ 53 | 54 | static void ufsk12_init(struct demod_state *s) 55 | { 56 | float f; 57 | int i; 58 | 59 | uart_init(s); 60 | memset(&s->l1.ufsk12, 0, sizeof(s->l1.ufsk12)); 61 | for (f = 0, i = 0; i < CORRLEN; i++) { 62 | corr_mark_i[i] = cos(f); 63 | corr_mark_q[i] = sin(f); 64 | f += 2.0*M_PI*FREQ_MARK/FREQ_SAMP; 65 | } 66 | for (f = 0, i = 0; i < CORRLEN; i++) { 67 | corr_space_i[i] = cos(f); 68 | corr_space_q[i] = sin(f); 69 | f += 2.0*M_PI*FREQ_SPACE/FREQ_SAMP; 70 | } 71 | } 72 | 73 | /* ---------------------------------------------------------------------- */ 74 | 75 | static void ufsk12_demod(struct demod_state *s, buffer_t buffer, int length) 76 | { 77 | float f; 78 | unsigned char curbit; 79 | 80 | if (s->l1.ufsk12.subsamp) { 81 | if (length <= (int)s->l1.ufsk12.subsamp) { 82 | s->l1.ufsk12.subsamp -= length; 83 | return; 84 | } 85 | buffer.fbuffer += s->l1.ufsk12.subsamp; 86 | length -= s->l1.ufsk12.subsamp; 87 | s->l1.ufsk12.subsamp = 0; 88 | } 89 | for (; length > 0; length -= SUBSAMP, buffer.fbuffer += SUBSAMP) { 90 | f = fsqr(mac(buffer.fbuffer, corr_mark_i, CORRLEN)) + 91 | fsqr(mac(buffer.fbuffer, corr_mark_q, CORRLEN)) - 92 | fsqr(mac(buffer.fbuffer, corr_space_i, CORRLEN)) - 93 | fsqr(mac(buffer.fbuffer, corr_space_q, CORRLEN)); 94 | s->l1.ufsk12.dcd_shreg <<= 1; 95 | s->l1.ufsk12.dcd_shreg |= (f > 0); 96 | verbprintf(10, "%c", '0'+(s->l1.ufsk12.dcd_shreg & 1)); 97 | /* 98 | * check if transition 99 | */ 100 | if ((s->l1.ufsk12.dcd_shreg ^ (s->l1.ufsk12.dcd_shreg >> 1)) & 1) { 101 | if (s->l1.ufsk12.sphase < (0x8000u-(SPHASEINC/2))) 102 | s->l1.ufsk12.sphase += SPHASEINC/8; 103 | else 104 | s->l1.ufsk12.sphase -= SPHASEINC/8; 105 | } 106 | s->l1.ufsk12.sphase += SPHASEINC; 107 | if (s->l1.ufsk12.sphase >= 0x10000u) { 108 | s->l1.ufsk12.sphase &= 0xffffu; 109 | curbit = s->l1.ufsk12.dcd_shreg & 1; 110 | verbprintf(9, " %c ", '0'+curbit); 111 | uart_rxbit(s, curbit); 112 | } 113 | } 114 | s->l1.ufsk12.subsamp = -length; 115 | } 116 | 117 | /* ---------------------------------------------------------------------- */ 118 | 119 | const struct demod_param demod_ufsk1200 = { 120 | "UFSK1200", true, FREQ_SAMP, CORRLEN, ufsk12_init, ufsk12_demod, NULL 121 | }; 122 | 123 | /* ---------------------------------------------------------------------- */ 124 | -------------------------------------------------------------------------------- /demod_clipfsk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_clipfsk.c -- 1200 baud FSK demodulator 3 | * 4 | * Copyright (C) 2007, 2024 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "multimon.h" 24 | #include "filter.h" 25 | #include 26 | #include 27 | 28 | /* ---------------------------------------------------------------------- */ 29 | 30 | /* 31 | * V.23 signaling 1200 baud +/- 1% 32 | * Mark frequency: 2300 Hz +/- 1.5% 33 | * Space frequency: 1100 Hz +/- 1.5% 34 | */ 35 | 36 | #define FREQ_MARK 1200 37 | #define FREQ_SPACE 2200 38 | #define FREQ_SAMP 22050 39 | #define BAUD 1200 40 | #define SUBSAMP 2 41 | 42 | /* ---------------------------------------------------------------------- */ 43 | 44 | #define CORRLEN ((int)(FREQ_SAMP/BAUD)) 45 | #define SPHASEINC (0x10000u*BAUD*SUBSAMP/FREQ_SAMP) 46 | 47 | static float corr_mark_i[CORRLEN]; 48 | static float corr_mark_q[CORRLEN]; 49 | static float corr_space_i[CORRLEN]; 50 | static float corr_space_q[CORRLEN]; 51 | 52 | /* ---------------------------------------------------------------------- */ 53 | 54 | static void clipfsk_init(struct demod_state *s) 55 | { 56 | float f; 57 | int i; 58 | 59 | clip_init(s); 60 | memset(&s->l1.clipfsk, 0, sizeof(s->l1.clipfsk)); 61 | for (f = 0, i = 0; i < CORRLEN; i++) { 62 | corr_mark_i[i] = cos(f); 63 | corr_mark_q[i] = sin(f); 64 | f += 2.0*M_PI*FREQ_MARK/FREQ_SAMP; 65 | } 66 | for (f = 0, i = 0; i < CORRLEN; i++) { 67 | corr_space_i[i] = cos(f); 68 | corr_space_q[i] = sin(f); 69 | f += 2.0*M_PI*FREQ_SPACE/FREQ_SAMP; 70 | } 71 | } 72 | 73 | /* ---------------------------------------------------------------------- */ 74 | 75 | static void clipfsk_demod(struct demod_state *s, buffer_t buffer, int length) 76 | { 77 | float f; 78 | unsigned char curbit; 79 | 80 | if (s->l1.clipfsk.subsamp) { 81 | if (length <= (int)s->l1.clipfsk.subsamp) { 82 | s->l1.clipfsk.subsamp -= length; 83 | return; 84 | } 85 | buffer.fbuffer += s->l1.clipfsk.subsamp; 86 | length -= s->l1.clipfsk.subsamp; 87 | s->l1.clipfsk.subsamp = 0; 88 | } 89 | for (; length > 0; length -= SUBSAMP, buffer.fbuffer += SUBSAMP) { 90 | f = fsqr(mac(buffer.fbuffer, corr_mark_i, CORRLEN)) + 91 | fsqr(mac(buffer.fbuffer, corr_mark_q, CORRLEN)) - 92 | fsqr(mac(buffer.fbuffer, corr_space_i, CORRLEN)) - 93 | fsqr(mac(buffer.fbuffer, corr_space_q, CORRLEN)); 94 | s->l1.clipfsk.dcd_shreg <<= 1; 95 | s->l1.clipfsk.dcd_shreg |= (f > 0); 96 | verbprintf(10, "%c", '0'+(s->l1.clipfsk.dcd_shreg & 1)); 97 | /* 98 | * check if transition 99 | */ 100 | if ((s->l1.clipfsk.dcd_shreg ^ (s->l1.clipfsk.dcd_shreg >> 1)) & 1) { 101 | if (s->l1.clipfsk.sphase < (0x8000u-(SPHASEINC/2))) 102 | s->l1.clipfsk.sphase += SPHASEINC/8; 103 | else 104 | s->l1.clipfsk.sphase -= SPHASEINC/8; 105 | } 106 | s->l1.clipfsk.sphase += SPHASEINC; 107 | if (s->l1.clipfsk.sphase >= 0x10000u) { 108 | s->l1.clipfsk.sphase &= 0xffffu; 109 | curbit = s->l1.clipfsk.dcd_shreg & 1; 110 | verbprintf(9, " %c ", '0'+curbit); 111 | clip_rxbit(s, curbit); 112 | } 113 | } 114 | s->l1.clipfsk.subsamp = -length; 115 | } 116 | 117 | /* ---------------------------------------------------------------------- */ 118 | 119 | const struct demod_param demod_clipfsk = { 120 | "CLIPFSK", true, FREQ_SAMP, CORRLEN, clipfsk_init, clipfsk_demod, NULL 121 | }; 122 | 123 | /* ---------------------------------------------------------------------- */ 124 | -------------------------------------------------------------------------------- /demod_afsk24.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_afsk24.c -- 2400 baud AFSK demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "multimon.h" 25 | #include "filter.h" 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | /* 32 | * Standard TCM3105 clock frequency: 4.4336MHz 33 | * Xtal used: 8 MHz 34 | * Ratio: 1.8044 35 | * Mark frequency: 3970 Hz 36 | * Space frequency: 2165 Hz 37 | */ 38 | 39 | #define FREQ_MARK 3970 40 | #define FREQ_SPACE 2165 41 | #define FREQ_SAMP 22050 42 | #define BAUD 2400 43 | 44 | /* ---------------------------------------------------------------------- */ 45 | 46 | #define CORRLEN (2*(int)(FREQ_SAMP/BAUD)) 47 | #define SPHASEINC (0x10000u*BAUD/FREQ_SAMP) 48 | 49 | static float corr_mark_i[CORRLEN]; 50 | static float corr_mark_q[CORRLEN]; 51 | static float corr_space_i[CORRLEN]; 52 | static float corr_space_q[CORRLEN]; 53 | 54 | /* ---------------------------------------------------------------------- */ 55 | 56 | static void afsk24_init(struct demod_state *s) 57 | { 58 | float f; 59 | int i; 60 | 61 | hdlc_init(s); 62 | memset(&s->l1.afsk24, 0, sizeof(s->l1.afsk24)); 63 | for (f = 0, i = 0; i < CORRLEN; i++) { 64 | corr_mark_i[i] = cos(f); 65 | corr_mark_q[i] = sin(f); 66 | f += 2.0*M_PI*FREQ_MARK/FREQ_SAMP; 67 | } 68 | for (f = 0, i = 0; i < CORRLEN; i++) { 69 | corr_space_i[i] = cos(f); 70 | corr_space_q[i] = sin(f); 71 | f += 2.0*M_PI*FREQ_SPACE/FREQ_SAMP; 72 | } 73 | for (i = 0; i < CORRLEN; i++) { 74 | f = 0.54 - 0.46*cos(2*M_PI*i/(float)(CORRLEN-1)); 75 | corr_mark_i[i] *= f; 76 | corr_mark_q[i] *= f; 77 | corr_space_i[i] *= f; 78 | corr_space_q[i] *= f; 79 | } 80 | } 81 | 82 | /* ---------------------------------------------------------------------- */ 83 | 84 | static void afsk24_demod(struct demod_state *s, buffer_t buffer, int length) 85 | { 86 | float f; 87 | unsigned char curbit; 88 | 89 | for (; length > 0; length--, buffer.fbuffer++) { 90 | f = fsqr(mac(buffer.fbuffer, corr_mark_i, CORRLEN)) + 91 | fsqr(mac(buffer.fbuffer, corr_mark_q, CORRLEN)) - 92 | fsqr(mac(buffer.fbuffer, corr_space_i, CORRLEN)) - 93 | fsqr(mac(buffer.fbuffer, corr_space_q, CORRLEN)); 94 | s->l1.afsk24.dcd_shreg <<= 1; 95 | s->l1.afsk24.dcd_shreg |= (f > 0); 96 | verbprintf(10, "%c", '0'+(s->l1.afsk24.dcd_shreg & 1)); 97 | /* 98 | * check if transition 99 | */ 100 | if ((s->l1.afsk24.dcd_shreg ^ (s->l1.afsk24.dcd_shreg >> 1)) & 1) { 101 | if (s->l1.afsk24.sphase < (0x8000u-(SPHASEINC/2))) 102 | s->l1.afsk24.sphase += SPHASEINC/8; 103 | else 104 | s->l1.afsk24.sphase -= SPHASEINC/8; 105 | } 106 | s->l1.afsk24.sphase += SPHASEINC; 107 | if (s->l1.afsk24.sphase >= 0x10000u) { 108 | s->l1.afsk24.sphase &= 0xffffu; 109 | s->l1.afsk24.lasts <<= 1; 110 | s->l1.afsk24.lasts |= s->l1.afsk24.dcd_shreg & 1; 111 | curbit = (s->l1.afsk24.lasts ^ 112 | (s->l1.afsk24.lasts >> 1) ^ 1) & 1; 113 | verbprintf(9, " %c ", '0'+curbit); 114 | hdlc_rxbit(s, curbit); 115 | } 116 | } 117 | } 118 | 119 | /* ---------------------------------------------------------------------- */ 120 | 121 | const struct demod_param demod_afsk2400 = { 122 | "AFSK2400", true, FREQ_SAMP, CORRLEN, afsk24_init, afsk24_demod, NULL 123 | }; 124 | 125 | /* ---------------------------------------------------------------------- */ 126 | -------------------------------------------------------------------------------- /demod_afsk24_2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_afsk24.c -- 2400 baud AFSK demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "multimon.h" 25 | #include "filter.h" 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | /* 32 | * Standard TCM3105 clock frequency: 4.4336MHz 33 | * Xtal used: 7.3728 MHz 34 | * Ratio: 1.6629 35 | * Mark frequency: 3658 Hz 36 | * Space frequency: 1996 Hz 37 | */ 38 | 39 | #define FREQ_MARK 3658 40 | #define FREQ_SPACE 1996 41 | #define FREQ_SAMP 22050 42 | #define BAUD 2400 43 | 44 | /* ---------------------------------------------------------------------- */ 45 | 46 | #define CORRLEN ((int)(2*FREQ_SAMP/BAUD)) 47 | #define SPHASEINC (0x10000u*BAUD/FREQ_SAMP) 48 | 49 | static float corr_mark_i[CORRLEN]; 50 | static float corr_mark_q[CORRLEN]; 51 | static float corr_space_i[CORRLEN]; 52 | static float corr_space_q[CORRLEN]; 53 | 54 | /* ---------------------------------------------------------------------- */ 55 | 56 | static void afsk24_2_init(struct demod_state *s) 57 | { 58 | float f; 59 | int i; 60 | 61 | hdlc_init(s); 62 | memset(&s->l1.afsk24, 0, sizeof(s->l1.afsk24)); 63 | for (f = 0, i = 0; i < CORRLEN; i++) { 64 | corr_mark_i[i] = cos(f); 65 | corr_mark_q[i] = sin(f); 66 | f += 2.0*M_PI*FREQ_MARK/FREQ_SAMP; 67 | } 68 | for (f = 0, i = 0; i < CORRLEN; i++) { 69 | corr_space_i[i] = cos(f); 70 | corr_space_q[i] = sin(f); 71 | f += 2.0*M_PI*FREQ_SPACE/FREQ_SAMP; 72 | } 73 | for (i = 0; i < CORRLEN; i++) { 74 | f = 0.54 - 0.46*cos(2*M_PI*i/(float)(CORRLEN-1)); 75 | corr_mark_i[i] *= f; 76 | corr_mark_q[i] *= f; 77 | corr_space_i[i] *= f; 78 | corr_space_q[i] *= f; 79 | } 80 | } 81 | 82 | /* ---------------------------------------------------------------------- */ 83 | 84 | static void afsk24_2_demod(struct demod_state *s, buffer_t buffer, int length) 85 | { 86 | float f; 87 | unsigned char curbit; 88 | 89 | for (; length > 0; length--, buffer.fbuffer++) { 90 | f = fsqr(mac(buffer.fbuffer, corr_mark_i, CORRLEN)) + 91 | fsqr(mac(buffer.fbuffer, corr_mark_q, CORRLEN)) - 92 | fsqr(mac(buffer.fbuffer, corr_space_i, CORRLEN)) - 93 | fsqr(mac(buffer.fbuffer, corr_space_q, CORRLEN)); 94 | s->l1.afsk24.dcd_shreg <<= 1; 95 | s->l1.afsk24.dcd_shreg |= (f > 0); 96 | verbprintf(10, "%c", '0'+(s->l1.afsk24.dcd_shreg & 1)); 97 | /* 98 | * check if transition 99 | */ 100 | if ((s->l1.afsk24.dcd_shreg ^ (s->l1.afsk24.dcd_shreg >> 1)) & 1) { 101 | if (s->l1.afsk24.sphase < (0x8000u-(SPHASEINC/2))) 102 | s->l1.afsk24.sphase += SPHASEINC/8; 103 | else 104 | s->l1.afsk24.sphase -= SPHASEINC/8; 105 | } 106 | s->l1.afsk24.sphase += SPHASEINC; 107 | if (s->l1.afsk24.sphase >= 0x10000u) { 108 | s->l1.afsk24.sphase &= 0xffffu; 109 | s->l1.afsk24.lasts <<= 1; 110 | s->l1.afsk24.lasts |= s->l1.afsk24.dcd_shreg & 1; 111 | curbit = (s->l1.afsk24.lasts ^ 112 | (s->l1.afsk24.lasts >> 1) ^ 1) & 1; 113 | verbprintf(9, " %c ", '0'+curbit); 114 | hdlc_rxbit(s, curbit); 115 | } 116 | } 117 | } 118 | 119 | /* ---------------------------------------------------------------------- */ 120 | 121 | const struct demod_param demod_afsk2400_2 = { 122 | "AFSK2400_2", true, FREQ_SAMP, CORRLEN, afsk24_2_init, afsk24_2_demod, NULL 123 | }; 124 | 125 | /* ---------------------------------------------------------------------- */ 126 | -------------------------------------------------------------------------------- /demod_afsk24_3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_afsk24.c -- 2400 baud AFSK demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "multimon.h" 25 | #include "filter.h" 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | /* 32 | * Standard TCM3105 clock frequency: 4.4336MHz 33 | * Xtal used: 6.5536 MHz 34 | * Ratio: 1.47816 35 | * Mark frequency: 3252 Hz 36 | * Space frequency: 1774 Hz 37 | */ 38 | 39 | #define FREQ_MARK 3252 40 | #define FREQ_SPACE 1774 41 | #define FREQ_SAMP 22050 42 | #define BAUD 2400 43 | 44 | /* ---------------------------------------------------------------------- */ 45 | 46 | #define CORRLEN ((int)(2*FREQ_SAMP/BAUD)) 47 | #define SPHASEINC (0x10000u*BAUD/FREQ_SAMP) 48 | 49 | static float corr_mark_i[CORRLEN]; 50 | static float corr_mark_q[CORRLEN]; 51 | static float corr_space_i[CORRLEN]; 52 | static float corr_space_q[CORRLEN]; 53 | 54 | /* ---------------------------------------------------------------------- */ 55 | 56 | static void afsk24_3_init(struct demod_state *s) 57 | { 58 | float f; 59 | int i; 60 | 61 | hdlc_init(s); 62 | memset(&s->l1.afsk24, 0, sizeof(s->l1.afsk24)); 63 | for (f = 0, i = 0; i < CORRLEN; i++) { 64 | corr_mark_i[i] = cos(f); 65 | corr_mark_q[i] = sin(f); 66 | f += 2.0*M_PI*FREQ_MARK/FREQ_SAMP; 67 | } 68 | for (f = 0, i = 0; i < CORRLEN; i++) { 69 | corr_space_i[i] = cos(f); 70 | corr_space_q[i] = sin(f); 71 | f += 2.0*M_PI*FREQ_SPACE/FREQ_SAMP; 72 | } 73 | for (i = 0; i < CORRLEN; i++) { 74 | f = 0.54 - 0.46*cos(2*M_PI*i/(float)(CORRLEN-1)); 75 | corr_mark_i[i] *= f; 76 | corr_mark_q[i] *= f; 77 | corr_space_i[i] *= f; 78 | corr_space_q[i] *= f; 79 | } 80 | } 81 | 82 | /* ---------------------------------------------------------------------- */ 83 | 84 | static void afsk24_3_demod(struct demod_state *s, buffer_t buffer, int length) 85 | { 86 | float f; 87 | unsigned char curbit; 88 | 89 | for (; length > 0; length--, buffer.fbuffer++) { 90 | f = fsqr(mac(buffer.fbuffer, corr_mark_i, CORRLEN)) + 91 | fsqr(mac(buffer.fbuffer, corr_mark_q, CORRLEN)) - 92 | fsqr(mac(buffer.fbuffer, corr_space_i, CORRLEN)) - 93 | fsqr(mac(buffer.fbuffer, corr_space_q, CORRLEN)); 94 | s->l1.afsk24.dcd_shreg <<= 1; 95 | s->l1.afsk24.dcd_shreg |= (f > 0); 96 | verbprintf(10, "%c", '0'+(s->l1.afsk24.dcd_shreg & 1)); 97 | /* 98 | * check if transition 99 | */ 100 | if ((s->l1.afsk24.dcd_shreg ^ (s->l1.afsk24.dcd_shreg >> 1)) & 1) { 101 | if (s->l1.afsk24.sphase < (0x8000u-(SPHASEINC/2))) 102 | s->l1.afsk24.sphase += SPHASEINC/8; 103 | else 104 | s->l1.afsk24.sphase -= SPHASEINC/8; 105 | } 106 | s->l1.afsk24.sphase += SPHASEINC; 107 | if (s->l1.afsk24.sphase >= 0x10000u) { 108 | s->l1.afsk24.sphase &= 0xffffu; 109 | s->l1.afsk24.lasts <<= 1; 110 | s->l1.afsk24.lasts |= s->l1.afsk24.dcd_shreg & 1; 111 | curbit = (s->l1.afsk24.lasts ^ 112 | (s->l1.afsk24.lasts >> 1) ^ 1) & 1; 113 | verbprintf(9, " %c ", '0'+curbit); 114 | hdlc_rxbit(s, curbit); 115 | } 116 | } 117 | } 118 | 119 | /* ---------------------------------------------------------------------- */ 120 | 121 | const struct demod_param demod_afsk2400_3 = { 122 | "AFSK2400_3", true, FREQ_SAMP, CORRLEN, afsk24_3_init, afsk24_3_demod, NULL 123 | }; 124 | 125 | /* ---------------------------------------------------------------------- */ 126 | -------------------------------------------------------------------------------- /gen_uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gen_uart.c -- generate DTMF sequences 3 | * 4 | * Copyright (C) 2007 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "gen.h" 24 | #include 25 | #include 26 | 27 | /*---------------------------------------------------------------------------*/ 28 | 29 | struct uarttx { 30 | unsigned int bitstream; 31 | unsigned int bitbuf; 32 | int numbits; 33 | }; 34 | 35 | static void txb_addbyte(struct gen_state *s, struct uarttx *uarttx, 36 | unsigned char bits, unsigned char nostart) 37 | { 38 | int i,j; 39 | if (uarttx->numbits >= 8) { 40 | if (s->s.uart.datalen >= sizeof(s->s.uart.data)) 41 | return; 42 | s->s.uart.data[s->s.uart.datalen++] = uarttx->bitbuf; 43 | uarttx->bitbuf >>= 8; 44 | uarttx->numbits -= 8; 45 | } 46 | 47 | if (!nostart) { 48 | uarttx->bitbuf &= ~(1 << uarttx->numbits); 49 | uarttx->numbits++; 50 | } 51 | uarttx->bitbuf |= bits << uarttx->numbits; 52 | uarttx->numbits += 8; 53 | uarttx->bitbuf |= 1 << uarttx->numbits; 54 | uarttx->numbits++; 55 | 56 | if (uarttx->numbits >= 8) { 57 | if (s->s.uart.datalen >= sizeof(s->s.uart.data)) 58 | return; 59 | s->s.uart.data[s->s.uart.datalen++] = uarttx->bitbuf; 60 | uarttx->bitbuf >>= 8; 61 | uarttx->numbits -= 8; 62 | } 63 | /* 64 | printf("bits:%02x\n", bits); 65 | for (i = 0; i < s->s.uart.datalen; i++) { 66 | // printf("%08X", s->s.uart.data[i]); 67 | for ( j = 0; j < 8; j++) { 68 | printf("%c", ((s->s.uart.data[i]) & (1<s.uart.bitmask = 1; 84 | for (i = 0; i < (p->p.uart.txdelay * (1200/100) / 8); i++) 85 | txb_addbyte(s, &uarttx, 0xff, 1); 86 | for (i = 0; i < 30; i++) 87 | txb_addbyte(s, &uarttx, 0x55, 0); 88 | for (i = 0; i < 18; i++) 89 | txb_addbyte(s, &uarttx, 0xff, 1); 90 | /* txb_addbyte(s, &uarttx, 0xff, 0); 91 | txb_addbyte(s, &uarttx, 0x00, 0); 92 | txb_addbyte(s, &uarttx, 0x01, 0); 93 | txb_addbyte(s, &uarttx, 0x02, 0); 94 | txb_addbyte(s, &uarttx, 0x03, 0); 95 | txb_addbyte(s, &uarttx, 0x00, 0); 96 | txb_addbyte(s, &uarttx, 0x55, 0); 97 | */ for (i = 0; i < p->p.uart.pktlen; i++) 98 | txb_addbyte(s, &uarttx, p->p.uart.pkt[i], 0); 99 | txb_addbyte(s, &uarttx, 0xff, 1); 100 | txb_addbyte(s, &uarttx, 0xff, 1); 101 | } 102 | 103 | int gen_uart(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s) 104 | { 105 | int num = 0; 106 | 107 | if (!s || s->s.uart.ch_idx < 0 || s->s.uart.ch_idx >= s->s.uart.datalen) 108 | return 0; 109 | for (; buflen > 0; buflen--, buf++, num++) { 110 | s->s.uart.bitph += 0x10000*1200 / SAMPLE_RATE; 111 | if (s->s.uart.bitph >= 0x10000u) { 112 | s->s.uart.bitph &= 0xffffu; 113 | s->s.uart.bitmask <<= 1; 114 | if (s->s.uart.bitmask >= 0x100) { 115 | s->s.uart.bitmask = 1; 116 | s->s.uart.ch_idx++; 117 | if (s->s.uart.ch_idx >= s->s.uart.datalen) 118 | return num; 119 | } 120 | s->s.uart.phinc = (s->s.uart.data[s->s.uart.ch_idx] & s->s.uart.bitmask) ? 121 | // 0x10000*120/SAMPLE_RATE : 0x10000*2200/SAMPLE_RATE; 122 | 0x10000*1200/SAMPLE_RATE : 0x10000*2200/SAMPLE_RATE; 123 | } 124 | *buf += (p->ampl * COS(s->s.uart.ph)) >> 15; 125 | s->s.uart.ph += s->s.uart.phinc; 126 | } 127 | return num; 128 | } 129 | 130 | 131 | -------------------------------------------------------------------------------- /gen_clipfsk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gen_clipfsk.c -- generate CLIP FSK 1200 bps sequences 3 | * 4 | * Copyright (C) 2007 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "gen.h" 24 | #include 25 | 26 | /*---------------------------------------------------------------------------*/ 27 | 28 | struct clipfsktx { 29 | unsigned int bitstream; 30 | unsigned int bitbuf; 31 | int numbits; 32 | }; 33 | 34 | static void txb_addbyte(struct gen_state *s, struct clipfsktx *clipfsktx, 35 | unsigned char bits, unsigned char nostart) 36 | { 37 | int i,j; 38 | if (clipfsktx->numbits >= 8) { 39 | if (s->s.clipfsk.datalen >= sizeof(s->s.clipfsk.data)) 40 | return; 41 | s->s.clipfsk.data[s->s.clipfsk.datalen++] = clipfsktx->bitbuf; 42 | clipfsktx->bitbuf >>= 8; 43 | clipfsktx->numbits -= 8; 44 | } 45 | 46 | if (!nostart) { 47 | clipfsktx->bitbuf &= ~(1 << clipfsktx->numbits); 48 | clipfsktx->numbits++; 49 | } 50 | clipfsktx->bitbuf |= bits << clipfsktx->numbits; 51 | clipfsktx->numbits += 8; 52 | clipfsktx->bitbuf |= 1 << clipfsktx->numbits; 53 | clipfsktx->numbits++; 54 | 55 | if (clipfsktx->numbits >= 8) { 56 | if (s->s.clipfsk.datalen >= sizeof(s->s.clipfsk.data)) 57 | return; 58 | s->s.clipfsk.data[s->s.clipfsk.datalen++] = clipfsktx->bitbuf; 59 | clipfsktx->bitbuf >>= 8; 60 | clipfsktx->numbits -= 8; 61 | } 62 | /* 63 | printf("bits:%02x\n", bits); 64 | for (i = 0; i < s->s.clipfsk.datalen; i++) { 65 | // printf("%08X", s->s.clipfsk.data[i]); 66 | for ( j = 0; j < 8; j++) { 67 | printf("%c", ((s->s.clipfsk.data[i]) & (1<s.clipfsk.bitmask = 1; 83 | // for (i = 0; i < (p->p.clipfsk.txdelay * (1200/100) / 8); i++) 84 | // txb_addbyte(s, &clipfsktx, 0xff, 1); 85 | for (i = 0; i < 30; i++) 86 | txb_addbyte(s, &clipfsktx, 0x55, 0); 87 | for (i = 0; i < 18; i++) 88 | txb_addbyte(s, &clipfsktx, 0xff, 1); 89 | for (i = 0; i < p->p.clipfsk.pktlen; i++) 90 | txb_addbyte(s, &clipfsktx, p->p.clipfsk.pkt[i], 0); 91 | /* The specifications are to stop carrier inmediatly with last stop bit, but an extra one 92 | is needed with this demodulator, so a full 10 bit stop must be generated */ 93 | txb_addbyte(s, &clipfsktx, 0xff, 1); 94 | } 95 | 96 | int gen_clipfsk(signed short *buf, int buflen, struct gen_params *p, struct gen_state *s) 97 | { 98 | int num = 0; 99 | 100 | if (!s || s->s.clipfsk.ch_idx < 0 || s->s.clipfsk.ch_idx >= s->s.clipfsk.datalen) 101 | return 0; 102 | for (; buflen > 0; buflen--, buf++, num++) { 103 | s->s.clipfsk.bitph += 0x10000*1200 / SAMPLE_RATE; 104 | if (s->s.clipfsk.bitph >= 0x10000u) { 105 | s->s.clipfsk.bitph &= 0xffffu; 106 | s->s.clipfsk.bitmask <<= 1; 107 | if (s->s.clipfsk.bitmask >= 0x100) { 108 | s->s.clipfsk.bitmask = 1; 109 | s->s.clipfsk.ch_idx++; 110 | if (s->s.clipfsk.ch_idx >= s->s.clipfsk.datalen) 111 | return num; 112 | } 113 | s->s.clipfsk.phinc = (s->s.clipfsk.data[s->s.clipfsk.ch_idx] & s->s.clipfsk.bitmask) ? 114 | // 0x10000*120/SAMPLE_RATE : 0x10000*2200/SAMPLE_RATE; 115 | 0x10000*1200/SAMPLE_RATE : 0x10000*2200/SAMPLE_RATE; 116 | } 117 | *buf += (p->ampl * COS(s->s.clipfsk.ph)) >> 15; 118 | s->s.clipfsk.ph += s->s.clipfsk.phinc; 119 | } 120 | return num; 121 | } 122 | 123 | 124 | -------------------------------------------------------------------------------- /demod_fmsfsk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_fmsfsk.c -- 1200 baud FMS FSK demodulator 3 | * 4 | * Copyright (C) 2014, 2024 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | /* ---------------------------------------------------------------------- */ 22 | 23 | #include "multimon.h" 24 | #include "filter.h" 25 | #include 26 | #include 27 | 28 | /* ---------------------------------------------------------------------- */ 29 | 30 | /* 31 | * FSM signaling 1200 baud +/- 0.01% 32 | * "1" frequency: 1200 Hz 33 | * "0" frequency: 1800 Hz 34 | */ 35 | 36 | #define FREQ_1 1200 37 | #define FREQ_0 1800 38 | #define FREQ_SAMP 22050 39 | #define BAUD 1200 40 | #define SUBSAMP 2 41 | 42 | /* ---------------------------------------------------------------------- */ 43 | 44 | #define CORRLEN ((int)(FREQ_SAMP/BAUD)) 45 | #define SPHASEINC (0x10000u*BAUD*SUBSAMP/FREQ_SAMP) 46 | 47 | static float corr_1_i[CORRLEN]; 48 | static float corr_1_q[CORRLEN]; 49 | static float corr_0_i[CORRLEN]; 50 | static float corr_0_q[CORRLEN]; 51 | 52 | /* ---------------------------------------------------------------------- */ 53 | 54 | static void fmsfsk_init(struct demod_state *s) 55 | { 56 | float f; 57 | int i; 58 | 59 | fms_init(s); 60 | memset(&s->l1.fmsfsk, 0, sizeof(s->l1.fmsfsk)); 61 | for (f = 0, i = 0; i < CORRLEN; i++) { 62 | corr_1_i[i] = cos(f); 63 | corr_1_q[i] = sin(f); 64 | f += 2.0*M_PI*FREQ_1/FREQ_SAMP; 65 | } 66 | for (f = 0, i = 0; i < CORRLEN; i++) { 67 | corr_0_i[i] = cos(f); 68 | corr_0_q[i] = sin(f); 69 | f += 2.0*M_PI*FREQ_0/FREQ_SAMP; 70 | } 71 | } 72 | 73 | /* ---------------------------------------------------------------------- */ 74 | 75 | static void fmsfsk_demod(struct demod_state *s, buffer_t buffer, int length) 76 | { 77 | float f; 78 | unsigned char curbit; 79 | 80 | if (s->l1.fmsfsk.subsamp) { 81 | if (length <= (int)s->l1.fmsfsk.subsamp) { 82 | s->l1.fmsfsk.subsamp -= length; 83 | return; 84 | } 85 | buffer.fbuffer += s->l1.fmsfsk.subsamp; 86 | length -= s->l1.fmsfsk.subsamp; 87 | s->l1.fmsfsk.subsamp = 0; 88 | } 89 | for (; length > 0; length -= SUBSAMP, buffer.fbuffer += SUBSAMP) { 90 | f = fsqr(mac(buffer.fbuffer, corr_1_i, CORRLEN)) + 91 | fsqr(mac(buffer.fbuffer, corr_1_q, CORRLEN)) - 92 | fsqr(mac(buffer.fbuffer, corr_0_i, CORRLEN)) - 93 | fsqr(mac(buffer.fbuffer, corr_0_q, CORRLEN)); 94 | s->l1.fmsfsk.dcd_shreg <<= 1; 95 | s->l1.fmsfsk.dcd_shreg |= (f > 0); 96 | verbprintf(10, "%c", '0'+(s->l1.fmsfsk.dcd_shreg & 1)); 97 | /* 98 | * check if transition 99 | */ 100 | if ((s->l1.fmsfsk.dcd_shreg ^ (s->l1.fmsfsk.dcd_shreg >> 1)) & 1) { 101 | if (s->l1.fmsfsk.sphase < (0x8000u-(SPHASEINC/2))) 102 | s->l1.fmsfsk.sphase += SPHASEINC/8; 103 | else 104 | s->l1.fmsfsk.sphase -= SPHASEINC/8; 105 | } 106 | s->l1.fmsfsk.sphase += SPHASEINC; 107 | if (s->l1.fmsfsk.sphase >= 0x10000u) { 108 | s->l1.fmsfsk.sphase &= 0xffffu; 109 | curbit = s->l1.fmsfsk.dcd_shreg & 1; 110 | verbprintf(9, "FMS %c ", '0'+curbit); 111 | fms_rxbit(s, curbit); 112 | } 113 | } 114 | s->l1.fmsfsk.subsamp = -length; 115 | } 116 | 117 | /* ---------------------------------------------------------------------- */ 118 | 119 | const struct demod_param demod_fmsfsk = { 120 | "FMSFSK", true, FREQ_SAMP, CORRLEN, fmsfsk_init, fmsfsk_demod, NULL 121 | }; 122 | 123 | /* ---------------------------------------------------------------------- */ 124 | -------------------------------------------------------------------------------- /demod_afsk12.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_afsk12.c -- 1200 baud AFSK demodulator 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * Copyright (C) 2024 7 | * Marat Fayzullin (luarvique@gmail.com) 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 | */ 23 | 24 | /* ---------------------------------------------------------------------- */ 25 | 26 | #include "multimon.h" 27 | #include "filter.h" 28 | #include 29 | #include 30 | 31 | /* ---------------------------------------------------------------------- */ 32 | 33 | /* 34 | * Standard TCM3105 clock frequency: 4.4336MHz 35 | * Mark frequency: 2200 Hz 36 | * Space frequency: 1200 Hz 37 | */ 38 | 39 | #define FREQ_MARK 1200 40 | #define FREQ_SPACE 2200 41 | #define FREQ_SAMP 22050 42 | #define BAUD 1200 43 | #define SUBSAMP 2 44 | 45 | /* ---------------------------------------------------------------------- */ 46 | 47 | #define CORRLEN ((int)(FREQ_SAMP/BAUD)) 48 | #define SPHASEINC (0x10000u*BAUD*SUBSAMP/FREQ_SAMP) 49 | 50 | static float corr_mark_i[CORRLEN]; 51 | static float corr_mark_q[CORRLEN]; 52 | static float corr_space_i[CORRLEN]; 53 | static float corr_space_q[CORRLEN]; 54 | 55 | /* ---------------------------------------------------------------------- */ 56 | 57 | static void afsk12_init(struct demod_state *s) 58 | { 59 | float f; 60 | int i; 61 | 62 | hdlc_init(s); 63 | memset(&s->l1.afsk12, 0, sizeof(s->l1.afsk12)); 64 | for (f = 0, i = 0; i < CORRLEN; i++) { 65 | corr_mark_i[i] = cos(f); 66 | corr_mark_q[i] = sin(f); 67 | f += 2.0*M_PI*FREQ_MARK/FREQ_SAMP; 68 | } 69 | for (f = 0, i = 0; i < CORRLEN; i++) { 70 | corr_space_i[i] = cos(f); 71 | corr_space_q[i] = sin(f); 72 | f += 2.0*M_PI*FREQ_SPACE/FREQ_SAMP; 73 | } 74 | } 75 | 76 | /* ---------------------------------------------------------------------- */ 77 | 78 | static void afsk12_demod(struct demod_state *s, buffer_t buffer, int length) 79 | { 80 | float f; 81 | unsigned char curbit; 82 | 83 | if (s->l1.afsk12.subsamp) { 84 | if (length <= (int)s->l1.afsk12.subsamp) { 85 | s->l1.afsk12.subsamp -= length; 86 | return; 87 | } 88 | buffer.fbuffer += s->l1.afsk12.subsamp; 89 | length -= s->l1.afsk12.subsamp; 90 | s->l1.afsk12.subsamp = 0; 91 | } 92 | for (; length > 0; length -= SUBSAMP, buffer.fbuffer += SUBSAMP) { 93 | f = fsqr(mac(buffer.fbuffer, corr_mark_i, CORRLEN)) + 94 | fsqr(mac(buffer.fbuffer, corr_mark_q, CORRLEN)) - 95 | fsqr(mac(buffer.fbuffer, corr_space_i, CORRLEN)) - 96 | fsqr(mac(buffer.fbuffer, corr_space_q, CORRLEN)); 97 | s->l1.afsk12.dcd_shreg <<= 1; 98 | s->l1.afsk12.dcd_shreg |= (f > 0); 99 | verbprintf(10, "%c", '0'+(s->l1.afsk12.dcd_shreg & 1)); 100 | /* 101 | * check if transition 102 | */ 103 | if ((s->l1.afsk12.dcd_shreg ^ (s->l1.afsk12.dcd_shreg >> 1)) & 1) { 104 | if (s->l1.afsk12.sphase < (0x8000u-(SPHASEINC/2))) 105 | s->l1.afsk12.sphase += SPHASEINC/8; 106 | else 107 | s->l1.afsk12.sphase -= SPHASEINC/8; 108 | } 109 | s->l1.afsk12.sphase += SPHASEINC; 110 | if (s->l1.afsk12.sphase >= 0x10000u) { 111 | s->l1.afsk12.sphase &= 0xffffu; 112 | s->l1.afsk12.lasts <<= 1; 113 | s->l1.afsk12.lasts |= s->l1.afsk12.dcd_shreg & 1; 114 | curbit = (s->l1.afsk12.lasts ^ 115 | (s->l1.afsk12.lasts >> 1) ^ 1) & 1; 116 | verbprintf(9, " %c ", '0'+curbit); 117 | hdlc_rxbit(s, curbit); 118 | } 119 | } 120 | s->l1.afsk12.subsamp = -length; 121 | } 122 | 123 | /* ---------------------------------------------------------------------- */ 124 | 125 | const struct demod_param demod_afsk1200 = { 126 | "AFSK1200", true, FREQ_SAMP, CORRLEN, afsk12_init, afsk12_demod, NULL 127 | }; 128 | 129 | /* ---------------------------------------------------------------------- */ 130 | -------------------------------------------------------------------------------- /demod_fsk96.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_fsk96.c -- FSK 9600 baud demodulator (G3RUH) 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* ---------------------------------------------------------------------- */ 23 | 24 | #include "multimon.h" 25 | #include "filter.h" 26 | #include 27 | #include 28 | 29 | /* ---------------------------------------------------------------------- */ 30 | 31 | #define FREQ_SAMP 22050 32 | #define BAUD 9600 33 | 34 | /* ---------------------------------------------------------------------- */ 35 | 36 | #define DESCRAM_TAP1 0x20000 37 | #define DESCRAM_TAP2 0x01000 38 | #define DESCRAM_TAP3 0x00001 39 | 40 | #define DESCRAM_TAPSH1 17 41 | #define DESCRAM_TAPSH2 12 42 | #define DESCRAM_TAPSH3 0 43 | 44 | #define SCRAM_TAP1 0x20000 /* X^17 */ 45 | #define SCRAM_TAPN 0x00021 /* X^0+X^5 */ 46 | 47 | /* --------------------------------------------------------------------- */ 48 | 49 | #define FILTLEN 24 50 | #define UPSAMP 3 51 | 52 | static const float inp_filt[3][24] = { 53 | { 0.000440, -0.001198, -0.000493, 0.003648, 54 | -0.000630, -0.008433, 0.005567, 0.015557, 55 | -0.019931, -0.026514, 0.079822, 0.181779, 56 | 0.124956, -0.002471, -0.032062, 0.008024, 57 | 0.012568, -0.006559, -0.004235, 0.003711, 58 | 0.000909, -0.001520, -0.000018, 0.000709}, 59 | { 0.000686, -0.000618, -0.001332, 0.002494, 60 | 0.002258, -0.007308, -0.001538, 0.016708, 61 | -0.004897, -0.035748, 0.034724, 0.161417, 62 | 0.161417, 0.034724, -0.035748, -0.004897, 63 | 0.016708, -0.001538, -0.007308, 0.002258, 64 | 0.002494, -0.001332, -0.000618, 0.000686}, 65 | { 0.000709, -0.000018, -0.001520, 0.000909, 66 | 0.003711, -0.004235, -0.006559, 0.012568, 67 | 0.008024, -0.032062, -0.002471, 0.124956, 68 | 0.181779, 0.079822, -0.026514, -0.019931, 69 | 0.015557, 0.005567, -0.008433, -0.000630, 70 | 0.003648, -0.000493, -0.001198, 0.000440} 71 | }; 72 | 73 | #define SPHASEINC (0x10000u*BAUD/FREQ_SAMP/UPSAMP) 74 | 75 | /* ---------------------------------------------------------------------- */ 76 | 77 | static void fsk96_init(struct demod_state *s) 78 | { 79 | hdlc_init(s); 80 | memset(&s->l1.fsk96, 0, sizeof(s->l1.fsk96)); 81 | } 82 | 83 | /* ---------------------------------------------------------------------- */ 84 | 85 | static void fsk96_demod(struct demod_state *s, buffer_t buffer, int length) 86 | { 87 | float f; 88 | unsigned char curbit; 89 | int i; 90 | unsigned int descx; 91 | 92 | for (; length > 0; length--, buffer.fbuffer++) { 93 | for (i = 0; i < UPSAMP; i++) { 94 | f = mac(buffer.fbuffer, inp_filt[i], FILTLEN); 95 | s->l1.fsk96.dcd_shreg <<= 1; 96 | s->l1.fsk96.dcd_shreg |= (f > 0); 97 | verbprintf(10, "%c", '0'+(s->l1.fsk96.dcd_shreg & 1)); 98 | /* 99 | * check if transition 100 | */ 101 | if ((s->l1.fsk96.dcd_shreg ^ (s->l1.fsk96.dcd_shreg >> 1)) & 1) { 102 | if (s->l1.fsk96.sphase < (0x8000u-(SPHASEINC/2))) 103 | s->l1.fsk96.sphase += SPHASEINC/8; 104 | else 105 | s->l1.fsk96.sphase -= SPHASEINC/8; 106 | } 107 | s->l1.fsk96.sphase += SPHASEINC; 108 | if (s->l1.fsk96.sphase >= 0x10000u) { 109 | s->l1.fsk96.sphase &= 0xffffu; 110 | s->l1.fsk96.descram <<= 1; 111 | s->l1.fsk96.descram |= s->l1.fsk96.dcd_shreg & 1; 112 | descx = s->l1.fsk96.descram ^ (s->l1.fsk96.descram >> 1); 113 | curbit = ((descx >> DESCRAM_TAPSH1) ^ (descx >> DESCRAM_TAPSH2) ^ 114 | (descx >> DESCRAM_TAPSH3) ^ 1) & 1; 115 | verbprintf(9, " %c ", '0'+curbit); 116 | hdlc_rxbit(s, curbit); 117 | } 118 | } 119 | } 120 | } 121 | 122 | /* ---------------------------------------------------------------------- */ 123 | 124 | const struct demod_param demod_fsk9600 = { 125 | "FSK9600", true, FREQ_SAMP, FILTLEN, fsk96_init, fsk96_demod, NULL 126 | }; 127 | 128 | /* ---------------------------------------------------------------------- */ 129 | -------------------------------------------------------------------------------- /selcall.c: -------------------------------------------------------------------------------- 1 | /* 2 | * selcall.c 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * Copyright (C) 2013 8 | * Elias Oenal (EliasOenal@gmail.com) 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 | */ 24 | 25 | /* ---------------------------------------------------------------------- */ 26 | 27 | #include "multimon.h" 28 | #include "filter.h" 29 | #include 30 | #include 31 | 32 | /* ---------------------------------------------------------------------- */ 33 | 34 | #define SAMPLE_RATE 22050 35 | #define BLOCKLEN (SAMPLE_RATE/100) /* 10ms blocks */ 36 | #define BLOCKNUM 4 /* must match numbers in multimon.h */ 37 | #define TIMEOUT_LIMIT 5 //50ms 38 | 39 | void selcall_init(struct demod_state *s) 40 | { 41 | memset(&s->l1.selcall, 0, sizeof(s->l1.selcall)); 42 | } 43 | 44 | void selcall_deinit(struct demod_state *s) 45 | { 46 | if(s->l1.selcall.timeout != 0) 47 | verbprintf(0, "\n"); 48 | } 49 | 50 | int find_max_idx(const float *f) 51 | { 52 | float en = 0; 53 | int idx = -1, i; 54 | 55 | for (i = 0; i < 16; i++) 56 | if (f[i] > en) { 57 | en = f[i]; 58 | idx = i; 59 | } 60 | if (idx < 0) 61 | return -1; 62 | en *= 0.1; 63 | for (i = 0; i < 16; i++) 64 | if (idx != i && f[i] > en) 65 | return -1; 66 | return idx; 67 | } 68 | 69 | static inline int process_block(struct demod_state *s) 70 | { 71 | float tote; 72 | float totte[32]; 73 | int i, j; 74 | 75 | tote = 0; 76 | for (i = 0; i < BLOCKNUM; i++) 77 | tote += s->l1.selcall.energy[i]; 78 | for (i = 0; i < 32; i++) { 79 | totte[i] = 0; 80 | for (j = 0; j < BLOCKNUM; j++) 81 | totte[i] += s->l1.selcall.tenergy[j][i]; 82 | } 83 | for (i = 0; i < 16; i++) 84 | totte[i] = fsqr(totte[i]) + fsqr(totte[i+16]); 85 | memmove(s->l1.selcall.energy+1, s->l1.selcall.energy, 86 | sizeof(s->l1.selcall.energy) - sizeof(s->l1.selcall.energy[0])); 87 | s->l1.selcall.energy[0] = 0; 88 | memmove(s->l1.selcall.tenergy+1, s->l1.selcall.tenergy, 89 | sizeof(s->l1.selcall.tenergy) - sizeof(s->l1.selcall.tenergy[0])); 90 | memset(s->l1.selcall.tenergy, 0, sizeof(s->l1.selcall.tenergy[0])); 91 | tote *= (BLOCKNUM*BLOCKLEN*0.5); /* adjust for block lengths */ 92 | verbprintf(10, "selcall: Energies: %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f" 93 | " %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f\n", 94 | tote, totte[0], totte[1], totte[2], totte[3], totte[4], totte[5], totte[6], totte[7], 95 | totte[8], totte[9], totte[10], totte[11], totte[12], totte[13], totte[14], totte[15]); 96 | if ((i = find_max_idx(totte)) < 0) 97 | return -1; 98 | if ((tote * 0.4) > totte[i]) 99 | return -1; 100 | return i; 101 | } 102 | 103 | void selcall_demod(struct demod_state *s, const float *buffer, int length, 104 | const unsigned int *selcall_freq, const char * const name) 105 | { 106 | float s_in; 107 | int i; 108 | 109 | for (; length > 0; length--, buffer++) { 110 | s_in = *buffer; 111 | s->l1.selcall.energy[0] += fsqr(s_in); 112 | for (i = 0; i < 16; i++) { 113 | s->l1.selcall.tenergy[0][i] += COS(s->l1.selcall.ph[i]) * s_in; 114 | s->l1.selcall.tenergy[0][i+16] += SIN(s->l1.selcall.ph[i]) * s_in; 115 | s->l1.selcall.ph[i] += selcall_freq[i]; 116 | } 117 | if ((s->l1.selcall.blkcount--) <= 0) { 118 | s->l1.selcall.blkcount = BLOCKLEN; 119 | i = process_block(s); 120 | if (i != s->l1.selcall.lastch && i >= 0) 121 | { 122 | if(s->l1.selcall.timeout == 0) 123 | verbprintf(0, "%s: ", name); 124 | verbprintf(0, "%1X", i); 125 | s->l1.selcall.timeout = 1; 126 | } 127 | 128 | if(i == -1 && s->l1.selcall.timeout != 0) 129 | s->l1.selcall.timeout++; 130 | if(s->l1.selcall.timeout > TIMEOUT_LIMIT+1) 131 | { 132 | verbprintf(0, "\n"); 133 | s->l1.selcall.timeout = 0; 134 | } 135 | 136 | s->l1.selcall.lastch = i; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /win32_getopt.h: -------------------------------------------------------------------------------- 1 | /* Declarations for getopt. 2 | Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2009 3 | Free Software Foundation, Inc. 4 | This file is part of the GNU C Library. 5 | 6 | The GNU C Library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Lesser General Public 8 | License as published by the Free Software Foundation; either 9 | version 2.1 of the License, or (at your option) any later version. 10 | 11 | The GNU C Library is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public 17 | License along with the GNU C Library; if not, write to the Free 18 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 19 | 02111-1307 USA. */ 20 | 21 | /* Getopt for Microsoft C 22 | This code is a modification of the Free Software Foundation, Inc. 23 | Getopt library for parsing command line argument the purpose was 24 | to provide a Microsoft Visual C friendly derivative. This code 25 | provides functionality for both Unicode and Multibyte builds. 26 | 27 | Date: 02/03/2011 - Ludvik Jerabek - Initial Release 28 | Version: 1.0 29 | Comment: Supports getopt, getopt_long, and getopt_long_only 30 | and POSIXLY_CORRECT environment flag 31 | License: LGPL 32 | 33 | Revisions: 34 | 35 | 02/03/2011 - Ludvik Jerabek - Initial Release 36 | 02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4 37 | 07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs 38 | 08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception 39 | 08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB 40 | 02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file 41 | 42 | **DISCLAIMER** 43 | THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 44 | EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE 45 | IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 46 | PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE 47 | EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT 48 | APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY 49 | DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY 50 | USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST 51 | PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON 52 | YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE 53 | EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 54 | */ 55 | #ifndef __GETOPT_H_ 56 | #define __GETOPT_H_ 57 | 58 | #ifdef _GETOPT_API 59 | #undef _GETOPT_API 60 | #endif 61 | 62 | #if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT) 63 | #error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually" 64 | #elif defined(STATIC_GETOPT) 65 | #pragma message("Warning static builds of getopt violate the Lesser GNU Public License") 66 | #define _GETOPT_API 67 | #elif defined(EXPORTS_GETOPT) 68 | #pragma message("Exporting getopt library") 69 | #define _GETOPT_API __declspec(dllexport) 70 | #else 71 | #pragma message("Importing getopt library") 72 | #define _GETOPT_API __declspec(dllimport) 73 | #endif 74 | 75 | 76 | #include 77 | 78 | // Standard GNU options 79 | #define null_argument 0 /*Argument Null*/ 80 | #define no_argument 0 /*Argument Switch Only*/ 81 | #define required_argument 1 /*Argument Required*/ 82 | #define optional_argument 2 /*Argument Optional*/ 83 | 84 | // Shorter Versions of options 85 | #define ARG_NULL 0 /*Argument Null*/ 86 | #define ARG_NONE 0 /*Argument Switch Only*/ 87 | #define ARG_REQ 1 /*Argument Required*/ 88 | #define ARG_OPT 2 /*Argument Optional*/ 89 | 90 | // Change behavior for C\C++ 91 | #ifdef __cplusplus 92 | #define _BEGIN_EXTERN_C extern "C" { 93 | #define _END_EXTERN_C } 94 | #define _GETOPT_THROW throw() 95 | #else 96 | #define _BEGIN_EXTERN_C 97 | #define _END_EXTERN_C 98 | #define _GETOPT_THROW 99 | #endif 100 | 101 | _BEGIN_EXTERN_C 102 | 103 | extern _GETOPT_API TCHAR *optarg; 104 | extern _GETOPT_API int optind; 105 | extern _GETOPT_API int opterr; 106 | extern _GETOPT_API int optopt; 107 | 108 | struct option 109 | { 110 | const TCHAR* name; 111 | int has_arg; 112 | int *flag; 113 | TCHAR val; 114 | }; 115 | 116 | extern _GETOPT_API int getopt(int argc, TCHAR *const *argv, const TCHAR *optstring) _GETOPT_THROW; 117 | extern _GETOPT_API int getopt_long(int ___argc, TCHAR *const *___argv, const TCHAR *__shortopts, const struct option *__longopts, int *__longind) _GETOPT_THROW; 118 | extern _GETOPT_API int getopt_long_only(int ___argc, TCHAR *const *___argv, const TCHAR *__shortopts, const struct option *__longopts, int *__longind) _GETOPT_THROW; 119 | _END_EXTERN_C 120 | 121 | // Undefine so the macros are not included 122 | #undef _BEGIN_EXTERN_C 123 | #undef _END_EXTERN_C 124 | #undef _GETOPT_THROW 125 | #undef _GETOPT_API 126 | 127 | #endif // __GETOPT_H_ 128 | -------------------------------------------------------------------------------- /demod_dtmf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * demod_dtmf.c -- DTMF signalling demodulator/decoder 3 | * 4 | * Copyright (C) 1996 5 | * Thomas Sailer (sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu) 6 | * 7 | * Copyright (C) 2024 8 | * Jason Lingohr (jason@lucid.net.au) 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 | */ 24 | 25 | /* ---------------------------------------------------------------------- */ 26 | 27 | #include "multimon.h" 28 | #include "filter.h" 29 | #include 30 | #include 31 | #include 32 | 33 | #include "cJSON.h" 34 | 35 | /* ---------------------------------------------------------------------- */ 36 | 37 | /* 38 | * 39 | * DTMF frequencies 40 | * 41 | * 1209 1336 1477 1633 42 | * 697 1 2 3 A 43 | * 770 4 5 6 B 44 | * 852 7 8 9 C 45 | * 941 * 0 # D 46 | * 47 | */ 48 | 49 | #define SAMPLE_RATE 22050 50 | #define BLOCKLEN (SAMPLE_RATE/100) /* 10ms blocks */ 51 | #define BLOCKNUM 4 /* must match numbers in multimon.h */ 52 | 53 | #define PHINC(x) ((x)*0x10000/SAMPLE_RATE) 54 | 55 | static const char *dtmf_transl = "123A456B789C*0#D"; 56 | 57 | static const unsigned int dtmf_phinc[8] = { 58 | PHINC(1209), PHINC(1336), PHINC(1477), PHINC(1633), 59 | PHINC(697), PHINC(770), PHINC(852), PHINC(941) 60 | }; 61 | 62 | extern int json_mode; 63 | 64 | /* ---------------------------------------------------------------------- */ 65 | 66 | static void dtmf_init(struct demod_state *s) 67 | { 68 | memset(&s->l1.dtmf, 0, sizeof(s->l1.dtmf)); 69 | } 70 | 71 | /* ---------------------------------------------------------------------- */ 72 | 73 | static int find_max_idx(const float *f) 74 | { 75 | float en = 0; 76 | int idx = -1, i; 77 | 78 | for (i = 0; i < 4; i++) 79 | if (f[i] > en) { 80 | en = f[i]; 81 | idx = i; 82 | } 83 | if (idx < 0) 84 | return -1; 85 | en *= 0.1; 86 | for (i = 0; i < 4; i++) 87 | if (idx != i && f[i] > en) 88 | return -1; 89 | return idx; 90 | } 91 | 92 | /* ---------------------------------------------------------------------- */ 93 | 94 | static inline int process_block(struct demod_state *s) 95 | { 96 | float tote; 97 | float totte[16]; 98 | int i, j; 99 | 100 | tote = 0; 101 | for (i = 0; i < BLOCKNUM; i++) 102 | tote += s->l1.dtmf.energy[i]; 103 | for (i = 0; i < 16; i++) { 104 | totte[i] = 0; 105 | for (j = 0; j < BLOCKNUM; j++) 106 | totte[i] += s->l1.dtmf.tenergy[j][i]; 107 | } 108 | for (i = 0; i < 8; i++) 109 | totte[i] = fsqr(totte[i]) + fsqr(totte[i+8]); 110 | memmove(s->l1.dtmf.energy+1, s->l1.dtmf.energy, 111 | sizeof(s->l1.dtmf.energy) - sizeof(s->l1.dtmf.energy[0])); 112 | s->l1.dtmf.energy[0] = 0; 113 | memmove(s->l1.dtmf.tenergy+1, s->l1.dtmf.tenergy, 114 | sizeof(s->l1.dtmf.tenergy) - sizeof(s->l1.dtmf.tenergy[0])); 115 | memset(s->l1.dtmf.tenergy, 0, sizeof(s->l1.dtmf.tenergy[0])); 116 | tote *= (BLOCKNUM*BLOCKLEN*0.5); /* adjust for block lengths */ 117 | verbprintf(10, "DTMF: Energies: %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f %8.5f\n", 118 | tote, totte[0], totte[1], totte[2], totte[3], totte[4], totte[5], totte[6], totte[7]); 119 | if ((i = find_max_idx(totte)) < 0) 120 | return -1; 121 | if ((j = find_max_idx(totte+4)) < 0) 122 | return -1; 123 | if ((tote * 0.4) > (totte[i] + totte[j+4])) 124 | return -1; 125 | if (totte[i]*3.0 0; length--, buffer.fbuffer++) { 140 | s_in = *buffer.fbuffer; 141 | s->l1.dtmf.energy[0] += fsqr(s_in); 142 | for (i = 0; i < 8; i++) { 143 | s->l1.dtmf.tenergy[0][i] += COS(s->l1.dtmf.ph[i]) * s_in; 144 | s->l1.dtmf.tenergy[0][i+8] += SIN(s->l1.dtmf.ph[i]) * s_in; 145 | s->l1.dtmf.ph[i] += dtmf_phinc[i]; 146 | } 147 | if ((s->l1.dtmf.blkcount--) <= 0) { 148 | s->l1.dtmf.blkcount = BLOCKLEN; 149 | i = process_block(s); 150 | if (i != s->l1.dtmf.lastch && i >= 0) { 151 | if (!json_mode) { 152 | verbprintf(0, "DTMF: %c\n", dtmf_transl[i]); 153 | } 154 | else { 155 | cJSON *json_output = cJSON_CreateObject(); 156 | cJSON_AddStringToObject(json_output, "demod_name", "DTMF"); 157 | char digit[2] = {dtmf_transl[i], '\0'}; 158 | cJSON_AddStringToObject(json_output, "digit", digit); 159 | addJsonTimestamp(json_output); 160 | fprintf(stdout, "%s\n", cJSON_PrintUnformatted(json_output)); 161 | cJSON_Delete(json_output); 162 | } 163 | } 164 | s->l1.dtmf.lastch = i; 165 | } 166 | } 167 | } 168 | 169 | /* ---------------------------------------------------------------------- */ 170 | 171 | const struct demod_param demod_dtmf = { 172 | "DTMF", true, SAMPLE_RATE, 0, dtmf_init, dtmf_demod, NULL 173 | }; 174 | 175 | /* ---------------------------------------------------------------------- */ 176 | -------------------------------------------------------------------------------- /unsupported/multimonNG.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | Win32Proj 15 | 16 | 17 | 18 | Application 19 | true 20 | 21 | 22 | Application 23 | false 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | true 37 | 38 | 39 | true 40 | 41 | 42 | 43 | WIN32;_DEBUG;_CONSOLE;NO_X11;ONLY_RAW;WIN32_AUDIO;_USE_MATH_DEFINES;WINDOWS;%(PreprocessorDefinitions) 44 | MultiThreadedDebugDLL 45 | Level3 46 | ProgramDatabase 47 | Disabled 48 | CompileAsCpp 49 | 50 | 51 | MachineX86 52 | true 53 | Console 54 | Winmm.lib;%(AdditionalDependencies) 55 | 56 | 57 | cl mkcostab.c -D_USE_MATH_DEFINES 58 | mkcostab.exe 59 | 60 | 61 | 62 | 63 | WIN32;NDEBUG;_CONSOLE;NO_X11;ONLY_RAW;WIN32_AUDIO;_USE_MATH_DEFINES;WINDOWS;%(PreprocessorDefinitions) 64 | MultiThreaded 65 | Level3 66 | ProgramDatabase 67 | CompileAsCpp 68 | 69 | 70 | MachineX86 71 | true 72 | Console 73 | true 74 | true 75 | Winmm.lib;%(AdditionalDependencies) 76 | 77 | 78 | cl mkcostab.c -D_USE_MATH_DEFINES 79 | mkcostab.exe 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /example/multipager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | ################################################## 4 | # GNU Radio Python Flow Graph 5 | # Title: Multipager 6 | # Generated: Thu Aug 3 14:04:34 2017 7 | ################################################## 8 | 9 | from gnuradio import analog 10 | from gnuradio import audio 11 | from gnuradio import blocks 12 | from gnuradio import eng_notation 13 | from gnuradio import filter 14 | from gnuradio import gr 15 | from gnuradio.eng_option import eng_option 16 | from gnuradio.filter import firdes 17 | from gnuradio.filter import pfb 18 | from optparse import OptionParser 19 | import osmosdr 20 | import subprocess 21 | import sip 22 | import sys 23 | import trollius as asyncio 24 | 25 | samplefile = 'sampler.raw' 26 | cmdpat = "sox -t raw -esigned-integer -b16 -r 22050 - -esigned-integer -b16 -r 22050 -t raw - | multimon-ng -t raw -q -a POCSAG512 -a POCSAG1200 -a POCSAG2400 -e -u -f alpha --timestamp --label \"%.3f MHz:\" -" 27 | 28 | class multipager(gr.top_block): 29 | 30 | def __init__(self): 31 | gr.top_block.__init__(self, "Multipager") 32 | ################################################## 33 | # Variables 34 | ################################################## 35 | self.volume = volume = 0.5 36 | self.squelch = squelch = -20 37 | self.sample_rate = sample_rate = 1e6 38 | self.out_scale = out_scale = 10000 39 | self.num_chan = num_chan = 20 40 | self.freq = freq = 148.664e6 41 | self.decim = decim = 2 42 | self.channel = channel = 19 43 | self.audio_rate = audio_rate = 50e3 44 | 45 | ################################################## 46 | # Blocks 47 | ################################################## 48 | if True: 49 | self.source = blocks.file_source(gr.sizeof_gr_complex*1, samplefile, True) 50 | else: 51 | self.source = osmosdr.source(args="hackrf") 52 | self.source.set_sample_rate(sample_rate) 53 | self.source.set_center_freq(freq, 0) 54 | self.source.set_freq_corr(0, 0) 55 | self.source.set_dc_offset_mode(0, 0) 56 | self.source.set_iq_balance_mode(0, 0) 57 | self.source.set_gain_mode(False, 0) 58 | self.source.set_gain(0, 0) 59 | self.source.set_if_gain(36, 0) 60 | self.source.set_bb_gain(44, 0) 61 | self.source.set_antenna("", 0) 62 | self.source.set_bandwidth(0, 0) 63 | 64 | self.pfb_channelizer_ccf_0 = pfb.channelizer_ccf( 65 | num_chan, 66 | (firdes.low_pass(1.0, sample_rate/decim, 7.25e3, 1.5e3, firdes.WIN_HAMMING, 6.76)), 67 | 1, 68 | 60) 69 | self.pfb_channelizer_ccf_0.set_channel_map(([])) 70 | self.pfb_channelizer_ccf_0.declare_sample_delay(0) 71 | 72 | self.low_pass_filter_0 = filter.fir_filter_ccf(decim, firdes.low_pass( 73 | 1, sample_rate, 400e3, 500e3, firdes.WIN_HAMMING, 6.76)) 74 | 75 | chwidth = sample_rate / decim / num_chan 76 | 77 | ################################################## 78 | # Connections 79 | ################################################## 80 | self.connect((self.source, 0), (self.low_pass_filter_0, 0)) 81 | self.connect((self.low_pass_filter_0, 0), (self.pfb_channelizer_ccf_0, 0)) 82 | 83 | # All channels 84 | sel = map(None, range(num_chan)) 85 | 86 | loop = asyncio.get_event_loop() 87 | self.fms = {} 88 | for i in range(num_chan): 89 | #fifopath = fifopat % (i) 90 | fifopath = None 91 | if i > num_chan / 2: 92 | chfreq = freq + chwidth * (i - num_chan) 93 | else: 94 | chfreq = freq + chwidth * i 95 | 96 | if i in sel: 97 | print("Channel %d %.3f MHz" % (i, chfreq / 1e6)) 98 | command = cmdpat % (chfreq / 1e6) 99 | fm = FMtoCommand(squelch, int(sample_rate / num_chan / decim), int(sample_rate / num_chan / decim), 5e3, 100 | out_scale, chfreq, command) 101 | 102 | self.connect((self.pfb_channelizer_ccf_0, i), (fm, 0)) 103 | self.fms[chfreq] = fm 104 | else: 105 | n = blocks.null_sink(gr.sizeof_gr_complex*1) 106 | self.connect((self.pfb_channelizer_ccf_0, i), (n, 0)) 107 | 108 | class FMtoCommand(gr.hier_block2): 109 | def __init__(self, squelch, quad_rate, audio_rate, max_dev, out_scale, freq, command): 110 | gr.hier_block2.__init__(self, "FMtoCommand", 111 | gr.io_signature(1, 1, gr.sizeof_gr_complex), 112 | gr.io_signature(0, 0, gr.sizeof_gr_complex)) 113 | 114 | analog_pwr_squelch = analog.pwr_squelch_cc(squelch, 1e-4, 0, True) 115 | analog_nbfm_rx = analog.nbfm_rx( 116 | audio_rate = audio_rate, 117 | quad_rate = quad_rate, 118 | tau = 75e-6, 119 | max_dev = max_dev, 120 | ) 121 | rational_resampler = filter.rational_resampler_fff( 122 | interpolation = 441, 123 | decimation = 500, 124 | taps = None, 125 | fractional_bw = None, 126 | ) 127 | blocks_float_to_short = blocks.float_to_short(1, out_scale) 128 | 129 | self.p = subprocess.Popen(command, shell = True, stdin = subprocess.PIPE) 130 | sink = blocks.file_descriptor_sink(gr.sizeof_short*1, self.p.stdin.fileno()) 131 | self.connect(self, (analog_pwr_squelch, 0)) 132 | self.connect((analog_pwr_squelch, 0), (analog_nbfm_rx, 0)) 133 | self.connect((analog_nbfm_rx, 0), (rational_resampler, 0)) 134 | self.connect((rational_resampler, 0), (blocks_float_to_short, 0)) 135 | self.connect((blocks_float_to_short, 0), (sink, 0)) 136 | 137 | def main(top_block_cls=multipager, options=None): 138 | 139 | tb = top_block_cls() 140 | tb.start() 141 | try: 142 | raw_input('Press Enter to quit: ') 143 | except EOFError: 144 | pass 145 | 146 | tb.stop() 147 | tb.wait() 148 | 149 | if __name__ == '__main__': 150 | main() 151 | -------------------------------------------------------------------------------- /multimon-ng.1: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .TH MULTIMON-NG 1 "February 22, 2025" 3 | .\" Please adjust this date whenever revising the manpage. 4 | .\" 5 | .\" Some roff macros, for reference: 6 | .\" .nh disable hyphenation 7 | .\" .hy enable hyphenation 8 | .\" .ad l left justify 9 | .\" .ad b justify to both left and right margins 10 | .\" .nf disable filling 11 | .\" .fi enable filling 12 | .\" .br insert line break 13 | .\" .sp insert n+1 empty lines 14 | .\" for manpage-specific macros, see man(7) 15 | .SH NAME 16 | multimon-ng \- program to decode radio transmissions 17 | .SH SYNOPSIS 18 | .B multimon-ng 19 | .RI [ options ]\ [input_file] 20 | .SH DESCRIPTION 21 | This manual page documents briefly the 22 | .B multimon-ng 23 | command. This manual page was written for Debian because the original 24 | program does not have a manual page. 25 | .PP 26 | .\" TeX users may be more comfortable with the \fB\fP and 27 | .\" \fI\fP escape sequences to invode bold face and italics, 28 | .\" respectively. 29 | The \fBmultimon-ng\fP software can decode a variety of digital transmission modes commonly found on VHF/UHF radio. A standard PC soundcard is used to acquire the signal from a transceiver. The decoding is done completely in software. Currently, the following modes are supported: 30 | .PP 31 | .RS 32 | .IP \(bu 4 33 | AX.25 34 | .RS 35 | .IP \(bu 4 36 | 1200 Baud AFSK 37 | .IP \(bu 4 38 | 2400 Baud AFSK (2 variants) 39 | .IP \(bu 4 40 | 4800 Baud HAPN 41 | .IP \(bu 4 42 | 9600 Baud FSK (G3RUH) 43 | .RE 44 | . 45 | .IP \(bu 4 46 | POCSAG 47 | .RS 48 | .IP \(bu 4 49 | 512 Baud 50 | .IP \(bu 4 51 | 1200 Baud 52 | .IP \(bu 4 53 | 2400 Baud 54 | .RE 55 | . 56 | .IP \(bu 4 57 | Selective call 58 | .RS 59 | .IP \(bu 4 60 | CCIR 61 | .IP \(bu 4 62 | EEA 63 | .IP \(bu 4 64 | EIA 65 | .RE 66 | . 67 | .IP \(bu 4 68 | Frequency Shift Keying 69 | .RS 70 | .IP \(bu 4 71 | 1200 Baud FSK (2200/1200 Hz) 72 | .IP \(bu 4 73 | 1200 Baud FSK (2300/1100 Hz) 74 | .IP \(bu 4 75 | 1200 baud FMS FSK 76 | .RE 77 | . 78 | .IP \(bu 4 79 | Miscellaneous 80 | .RS 81 | .IP \(bu 4 82 | DTMF 83 | .IP \(bu 4 84 | CW/Morse 85 | .IP \(bu 4 86 | EAS 87 | .IP \(bu 4 88 | FLEX 89 | .IP \(bu 4 90 | SCOPE 91 | .IP \(bu 4 92 | X10 93 | .IP \(bu 4 94 | ZVEI 95 | .RE 96 | .RE 97 | .PP 98 | An arbitrary set of the above modes may run concurrently on the same input signal (provided the CPU power is sufficient), so you do not have to know in advance which mode is used. Note however that some modes might require modifications to the radio (especially the 9600 baud FSK and the POCSAG modes) to work properly. 99 | .PP 100 | AX.25 - Amateur Packet Radio protocol datagram format. 101 | .br 102 | DTMF - Dual Tone Multi Frequency. Commonly used in in-band telephone dialing. 103 | .br 104 | EAS - Emergency Alert System. 105 | .br 106 | FLEX - a paging format. 107 | .br 108 | POCSAG (Post Office Code Standards Advisory Group) is a common paging transmission format. 109 | .br 110 | SCOPE - show samples in a rudimentary oscilloscope display on an X server. 111 | .br 112 | X10 - a home automation protocol. 113 | .br 114 | ZVEI - The German Electrical and Electronic Manufacturers' Association - paging tone format. 115 | .SH OPTIONS 116 | .TP 117 | .B \-t 118 | Input file type (any other type than raw requires sox). 119 | Allowed types: raw aiff au hcom sf voc cdr dat smp wav maud vwe. 120 | .TP 121 | .B \-a 122 | Add demodulator (see below). 123 | .TP 124 | .B \-s 125 | Subtract demodulator. 126 | .TP 127 | .B \-c 128 | Remove all demodulators (must be added with -a ). 129 | .TP 130 | .B \-q 131 | Quiet output messages. 132 | .TP 133 | .B \-v 134 | Verbosity level (0-10). 135 | For POCSAG and MORSE_CW '-v1' prints decoding statistics. 136 | .TP 137 | .B \-h 138 | Print the help. 139 | .TP 140 | .B \-A 141 | APRS mode (TNC2 text output). 142 | .TP 143 | .B \-m 144 | Mute SoX warnings. 145 | .TP 146 | .B \-m 147 | Call SoX in repeatable mode (e.g. fixed random seed for dithering). 148 | .TP 149 | .B \-n 150 | Don't flush stdout, increases performance. 151 | .TP 152 | .B \-j 153 | FMS: Just output hex data and CRC, no parsing. 154 | .TP 155 | .B \-e 156 | POCSAG: Hide empty messages. 157 | .TP 158 | .B \-u 159 | POCSAG: Heuristically prune unlikely decodes. 160 | .TP 161 | .B \-i 162 | POCSAG: Inverts the input samples. Try this if decoding fails. 163 | .TP 164 | .B \-p 165 | POCSAG: Show partially received messages. 166 | .TP 167 | .B \-f 168 | POCSAG: Disables auto-detection and forces decoding of data as 169 | ( can be 'numeric', 'alpha', 'skyper' and 'auto') 170 | .TP 171 | .B \-b 172 | POCSAG: BCH bit error correction level. Set 0 to disable, default is 2. 173 | Lower levels increase performance and lower false positives. 174 | .TP 175 | .B \-C 176 | POCSAG: Set charset (default: US), where is one of: 177 | US, FR, DE, SE, SI 178 | .TP 179 | .B \-o