mirror of https://github.com/wb2osz/direwolf.git
EAS SAME reception.
This commit is contained in:
parent
5fd8120de7
commit
e2b32d1d2a
|
@ -18,7 +18,9 @@
|
|||
|
||||
- "-X" option enables FX.25 transmission. FX.25 reception is always enabled so you don't need to do anything special. "What is FX.25?" you might ask. It is forward error correction (FEC) added in a way that is completely compatible with an ordinary AX.25 frame. See new document ***AX25\_plus\_FEC\_equals\_FX25.pdf*** for details.
|
||||
|
||||
- Receive AIS location data from ships. Enable by using "-B AIS" command line option or "MODEM AIS" in the configuration file. AIS NMEA sentences are encapsulated in APRS user-defined data with a "{DA" prefix. This uses 9600 bps so you need to use wide band audio, not what comes out of the speaker.
|
||||
- Receive AIS location data from ships. Enable by using "-B AIS" command line option or "MODEM AIS" in the configuration file. AIS NMEA sentences are encapsulated in APRS user-defined data with a "{DA" prefix. This uses 9600 bps so you need to use wide band audio, not what comes out of the speaker. There is also a "-A" option to generate APRS Object Reports.
|
||||
|
||||
- Receive Emergency Alert System (EAS) Specific Area Message Encoding (SAME). Enable by using "-B EAS" command line option or "MODEM EAS" in the configuration file. EAS SAME messages are encapsulated in APRS user-defined data with a "{DE" prefix. This uses low speed AFSK so speaker output is fine.
|
||||
|
||||
- "-t" option now accepts more values to accommodate inconsistent handling of text color control codes by different terminal emulators. The default, 1, should work with most modern terminal types. If the colors are not right, try "-t 9" to see the result of the different choices and pick the best one. If none of them look right, file a bug report and specify: operating system version (e.g. Raspbian Buster), terminal emulator type and version (e.g. LXTerminal 0.3.2). Include a screen capture.
|
||||
|
||||
|
|
12
src/atest.c
12
src/atest.c
|
@ -277,6 +277,9 @@ int main (int argc, char *argv[])
|
|||
if (strcasecmp(optarg, "AIS") == 0) {
|
||||
B_opt = 12345; // See special case below.
|
||||
}
|
||||
else if (strcasecmp(optarg, "EAS") == 0) {
|
||||
B_opt = 23456; // See special case below.
|
||||
}
|
||||
else {
|
||||
B_opt = atoi(optarg);
|
||||
}
|
||||
|
@ -464,6 +467,14 @@ int main (int argc, char *argv[])
|
|||
my_audio_config.achan[0].space_freq = 0;
|
||||
strlcpy (my_audio_config.achan[0].profiles, " ", sizeof(my_audio_config.achan[0].profiles)); // avoid getting default later.
|
||||
}
|
||||
else if (my_audio_config.achan[0].baud == 23456) {
|
||||
my_audio_config.achan[0].modem_type = MODEM_EAS;
|
||||
my_audio_config.achan[0].baud = 521; // Actually 520.83 but we have an integer field here.
|
||||
// Will make more precise in afsk demod init.
|
||||
my_audio_config.achan[0].mark_freq = 2083; // Actually 2083.3 - logic 1.
|
||||
my_audio_config.achan[0].space_freq = 1563; // Actually 1562.5 - logic 0.
|
||||
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
|
||||
}
|
||||
else {
|
||||
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||
my_audio_config.achan[0].mark_freq = 0;
|
||||
|
@ -945,6 +956,7 @@ static void usage (void) {
|
|||
dw_printf (" 4800 bps uses 8PSK based on V.27 standard.\n");
|
||||
dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n");
|
||||
dw_printf (" AIS for ship Automatic Identification System.\n");
|
||||
dw_printf (" EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME).\n");
|
||||
dw_printf ("\n");
|
||||
dw_printf (" -g Use G3RUH modem rather rather than default for data rate.\n");
|
||||
dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n");
|
||||
|
|
|
@ -148,7 +148,7 @@ struct audio_s {
|
|||
/* Could all be the same or different. */
|
||||
|
||||
|
||||
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS } modem_type;
|
||||
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS, MODEM_EAS } modem_type;
|
||||
|
||||
/* Usual AFSK. */
|
||||
/* Baseband signal. Not used yet. */
|
||||
|
|
11
src/config.c
11
src/config.c
|
@ -1279,6 +1279,9 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
if (strcasecmp(t,"AIS") == 0) {
|
||||
n = 12345; // See special case later.
|
||||
}
|
||||
else if (strcasecmp(t,"EAS") == 0) {
|
||||
n = 23456; // See special case later.
|
||||
}
|
||||
else {
|
||||
n = atoi(t);
|
||||
}
|
||||
|
@ -1328,6 +1331,14 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_audio_config->achan[channel].mark_freq = 0;
|
||||
p_audio_config->achan[channel].space_freq = 0;
|
||||
}
|
||||
else if (p_audio_config->achan[channel].baud == 23456) {
|
||||
p_audio_config->achan[channel].modem_type = MODEM_EAS;
|
||||
p_audio_config->achan[channel].baud = 521; // Actually 520.83 but we have an integer field here.
|
||||
// Will make more precise in afsk demod init.
|
||||
p_audio_config->achan[channel].mark_freq = 2083; // Actually 2083.3 - logic 1.
|
||||
p_audio_config->achan[channel].space_freq = 1563; // Actually 1562.5 - logic 0.
|
||||
// ? strlcpy (p_audio_config->achan[channel].profiles, "D", sizeof(p_audio_config->achan[channel].profiles));
|
||||
}
|
||||
else {
|
||||
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
|
||||
p_audio_config->achan[channel].mark_freq = 0;
|
||||
|
|
18
src/demod.c
18
src/demod.c
|
@ -133,6 +133,20 @@ int demod_init (struct audio_s *pa)
|
|||
break;
|
||||
|
||||
case MODEM_AFSK:
|
||||
case MODEM_EAS:
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
|
||||
if (save_audio_config_p->achan[chan].fix_bits != RETRY_NONE) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Channel %d: FIX_BITS option has been turned off for EAS.\n", chan);
|
||||
save_audio_config_p->achan[chan].fix_bits = RETRY_NONE;
|
||||
}
|
||||
if (save_audio_config_p->achan[chan].passall != 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Channel %d: PASSALL option has been turned off for EAS.\n", chan);
|
||||
save_audio_config_p->achan[chan].passall = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear apart the profile and put it back together in a normalized form:
|
||||
|
@ -954,6 +968,7 @@ void demod_process_sample (int chan, int subchan, int sam)
|
|||
break;
|
||||
|
||||
case MODEM_AFSK:
|
||||
case MODEM_EAS:
|
||||
|
||||
if (save_audio_config_p->achan[chan].decimate > 1) {
|
||||
|
||||
|
@ -1070,7 +1085,8 @@ alevel_t demod_get_audio_level (int chan, int subchan)
|
|||
|
||||
alevel.rec = (int) (( D->alevel_rec_peak - D->alevel_rec_valley ) * 50.0f + 0.5f);
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_AFSK) {
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_AFSK ||
|
||||
save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
|
||||
|
||||
/* For AFSK, we have mark and space amplitudes. */
|
||||
|
||||
|
|
|
@ -307,9 +307,18 @@ void demod_afsk_init (int samples_per_sec, int baud, int mark_freq,
|
|||
/*
|
||||
* Calculate constants used for timing.
|
||||
* The audio sample rate must be at least a few times the data rate.
|
||||
*
|
||||
* Baud is an integer so we hack in a fine ajustment for EAS.
|
||||
* Probably makes no difference because the DPLL keeps it in sync.
|
||||
*
|
||||
* A fraction if a Hz would make no difference for the filters.
|
||||
*/
|
||||
|
||||
D->pll_step_per_sample = (int) round((TICKS_PER_PLL_CYCLE * (double)baud) / ((double)samples_per_sec));
|
||||
if (baud == 521) {
|
||||
D->pll_step_per_sample = (int) round((TICKS_PER_PLL_CYCLE * (double)520.83) / ((double)samples_per_sec));
|
||||
}
|
||||
else {
|
||||
D->pll_step_per_sample = (int) round((TICKS_PER_PLL_CYCLE * (double)baud) / ((double)samples_per_sec));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert number of bit times to number of taps.
|
||||
|
|
|
@ -288,7 +288,7 @@ int main (int argc, char *argv[])
|
|||
text_color_init(t_opt);
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test 4\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
|
||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "F", __DATE__);
|
||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
|
||||
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
|
||||
|
@ -435,6 +435,9 @@ int main (int argc, char *argv[])
|
|||
if (strcasecmp(optarg, "AIS") == 0) {
|
||||
B_opt = 12345; // See special case below.
|
||||
}
|
||||
else if (strcasecmp(optarg, "EAS") == 0) {
|
||||
B_opt = 23456; // See special case below.
|
||||
}
|
||||
else {
|
||||
B_opt = atoi(optarg);
|
||||
}
|
||||
|
@ -749,6 +752,14 @@ int main (int argc, char *argv[])
|
|||
audio_config.achan[0].mark_freq = 0;
|
||||
audio_config.achan[0].space_freq = 0;
|
||||
}
|
||||
else if (audio_config.achan[0].baud == 23456) {
|
||||
audio_config.achan[0].modem_type = MODEM_EAS;
|
||||
audio_config.achan[0].baud = 521; // Actually 520.83 but we have an integer field here.
|
||||
// Will make more precise in afsk demod init.
|
||||
audio_config.achan[0].mark_freq = 2083; // Actually 2083.3 - logic 1.
|
||||
audio_config.achan[0].space_freq = 1563; // Actually 1562.5 - logic 0.
|
||||
strlcpy (audio_config.achan[0].profiles, "D", sizeof(audio_config.achan[0].profiles));
|
||||
}
|
||||
else {
|
||||
audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
|
||||
audio_config.achan[0].mark_freq = 0;
|
||||
|
@ -1451,6 +1462,7 @@ static void usage (char **argv)
|
|||
dw_printf (" 4800 bps uses 8PSK based on V.27 standard.\n");
|
||||
dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n");
|
||||
dw_printf (" AIS for ship Automatic Identification System.\n");
|
||||
dw_printf (" EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME).\n");
|
||||
dw_printf (" -g Force G3RUH modem regardless of speed.\n");
|
||||
dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n");
|
||||
dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n");
|
||||
|
|
231
src/hdlc_rec.c
231
src/hdlc_rec.c
|
@ -32,6 +32,7 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h> // uint64_t
|
||||
|
||||
//#include "tune.h"
|
||||
#include "demod.h"
|
||||
|
@ -103,6 +104,13 @@ struct hdlc_state_s {
|
|||
|
||||
rrbb_t rrbb; /* Handle for bit array for raw received bits. */
|
||||
|
||||
uint64_t eas_acc; /* Accumulate most recent 64 bits received for EAS. */
|
||||
|
||||
int eas_gathering; /* Decoding in progress. */
|
||||
|
||||
int eas_plus_found; /* "+" seen, indicating end of geographical area list. */
|
||||
|
||||
int eas_fields_after_plus; /* Number of "-" characters after the "+". */
|
||||
};
|
||||
|
||||
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
|
||||
|
@ -182,6 +190,216 @@ static int my_rand (void) {
|
|||
return (seed);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************
|
||||
*
|
||||
* Name: eas_rec_bit
|
||||
*
|
||||
* Purpose: Extract EAS trasmissions from a stream of bits.
|
||||
*
|
||||
* Inputs: chan - Channel number.
|
||||
*
|
||||
* subchan - This allows multiple demodulators per channel.
|
||||
*
|
||||
* slice - Allows multiple slicers per demodulator (subchannel).
|
||||
*
|
||||
* raw - One bit from the demodulator.
|
||||
* should be 0 or 1.
|
||||
*
|
||||
* future_use - Not implemented yet. PSK already provides it.
|
||||
*
|
||||
*
|
||||
* Description: This is called once for each received bit.
|
||||
* For each valid transmission, process_rec_frame()
|
||||
* is called for further processing.
|
||||
*
|
||||
***********************************************************************************/
|
||||
|
||||
#define PREAMBLE 0xababababababababULL
|
||||
#define PREAMBLE_ZCZC 0x435a435aababababULL
|
||||
#define PREAMBLE_NNNN 0x4e4e4e4eababababULL
|
||||
#define EAS_MAX_LEN 268 // Not including preamble. Up to 31 geographic areas.
|
||||
|
||||
|
||||
static void eas_rec_bit (int chan, int subchan, int slice, int raw, int future_use)
|
||||
{
|
||||
struct hdlc_state_s *H;
|
||||
|
||||
/*
|
||||
* Different state information for each channel / subchannel / slice.
|
||||
*/
|
||||
H = &hdlc_state[chan][subchan][slice];
|
||||
|
||||
//dw_printf ("slice %d = %d\n", slice, raw);
|
||||
|
||||
// Accumulate most recent 64 bits.
|
||||
|
||||
H->eas_acc >>= 1;
|
||||
if (raw) {
|
||||
H->eas_acc |= 0x8000000000000000ULL;
|
||||
}
|
||||
|
||||
int done = 0;
|
||||
|
||||
if (H->eas_acc == PREAMBLE_ZCZC) {
|
||||
//dw_printf ("ZCZC\n");
|
||||
H->olen = 0;
|
||||
H->eas_gathering = 1;
|
||||
H->eas_plus_found = 0;
|
||||
H->eas_fields_after_plus = 0;
|
||||
strlcpy ((char*)(H->frame_buf), "ZCZC", sizeof(H->frame_buf));
|
||||
H->frame_len = 4;
|
||||
}
|
||||
else if (H->eas_acc == PREAMBLE_NNNN) {
|
||||
//dw_printf ("NNNN\n");
|
||||
H->olen = 0;
|
||||
H->eas_gathering = 1;
|
||||
strlcpy ((char*)(H->frame_buf), "NNNN", sizeof(H->frame_buf));
|
||||
H->frame_len = 4;
|
||||
done = 1;
|
||||
}
|
||||
else if (H->eas_gathering) {
|
||||
H->olen++;
|
||||
if (H->olen == 8) {
|
||||
H->olen = 0;
|
||||
char ch = H->eas_acc >> 56;
|
||||
H->frame_buf[H->frame_len++] = ch;
|
||||
H->frame_buf[H->frame_len] = '\0';
|
||||
//dw_printf ("frame_buf = %s\n", H->frame_buf);
|
||||
|
||||
// What characters are acceptable?
|
||||
// Only ASCII is allowed. i.e. the MSB must be 0.
|
||||
// The examples show only digits but the geographical area can
|
||||
// contain anything in range of '!' to DEL or CR or LF.
|
||||
// There are no restrictions listed for the originator and
|
||||
// examples contain a slash.
|
||||
// It's not clear if a space can occur in other places.
|
||||
|
||||
if ( ! (( ch >= ' ' && ch <= 0x7f) || ch == '\r' || ch == '\n')) {
|
||||
//#define DEBUG_E 1
|
||||
#ifdef DEBUG_E
|
||||
dw_printf ("reject %d invalid character = %s\n", slice, H->frame_buf);
|
||||
#endif
|
||||
H->eas_gathering = 0;
|
||||
return;
|
||||
}
|
||||
if (H->frame_len > EAS_MAX_LEN) { // FIXME: look for other places with max length
|
||||
#ifdef DEBUG_E
|
||||
dw_printf ("reject %d too long = %s\n", slice, H->frame_buf);
|
||||
#endif
|
||||
H->eas_gathering = 0;
|
||||
return;
|
||||
}
|
||||
if (ch == '+') {
|
||||
H->eas_plus_found = 1;
|
||||
H->eas_fields_after_plus = 0;
|
||||
}
|
||||
if (H->eas_plus_found && ch == '-') {
|
||||
H->eas_fields_after_plus++;
|
||||
if (H->eas_fields_after_plus == 3) {
|
||||
done = 1; // normal case
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (done) {
|
||||
#ifdef DEBUG_E
|
||||
dw_printf ("frame_buf %d = %s\n", slice, H->frame_buf);
|
||||
#endif
|
||||
alevel_t alevel = demod_get_audio_level (chan, subchan);
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len, alevel, 0, 0);
|
||||
H->eas_gathering = 0;
|
||||
}
|
||||
|
||||
} // end eas_rec_bit
|
||||
|
||||
|
||||
/*
|
||||
|
||||
EAS has no error detection.
|
||||
Maybe that doesn't matter because we would normally be dealing with a reasonable
|
||||
VHF FM or TV signal.
|
||||
Let's see what happens when we intentionally introduce errors.
|
||||
When some match and others don't, the multislice voting should give preference
|
||||
to those matching others.
|
||||
|
||||
$ src/atest -P+ -B EAS -e 3e-3 ../../ref-doc/EAS/same.wav
|
||||
Demodulator profile set to "+"
|
||||
96000 samples per second. 16 bits per sample. 1 audio channels.
|
||||
2079360 audio bytes in file. Duration = 10.8 seconds.
|
||||
Fix Bits level = 0
|
||||
Channel 0: 521 baud, AFSK 2083 & 1563 Hz, D+, 96000 sample rate / 3.
|
||||
|
||||
case 1: Slice 6 is different than others (EQS vs. EAS) so we want one of the others that match.
|
||||
Slice 3 has an unexpected character (in 0120u7) so it is a mismatch.
|
||||
At this point we are not doing validity checking other than all printable characters.
|
||||
|
||||
We are left with 0 & 4 which don't match (012057 vs. 012077).
|
||||
So I guess we don't have any two that match so it is a toss up.
|
||||
|
||||
reject 7 invalid character = ZCZC-EAS-RWT-0120▒
|
||||
reject 5 invalid character = ZCZC-ECW-RWT-012057-012081-012101-012103-012115+003
|
||||
frame_buf 6 = ZCZC-EQS-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
frame_buf 4 = ZCZC-EAS-RWT-012077-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
frame_buf 3 = ZCZC-EAS-RWT-0120u7-012281-012101-012103-092115+0038-2780415-VTSP/TV-
|
||||
frame_buf 0 = ZCZC-EAS-RWT-012057-412081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
|
||||
DECODED[1] 0:01.313 EAS audio level = 194(106/108) |__||_|__
|
||||
[0.0] EAS>APDW16:{DEZCZC-EAS-RWT-012057-412081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
|
||||
Case 2: We have two that match so pick either one.
|
||||
|
||||
reject 5 invalid character = ZCZC-EAS-RW▒
|
||||
reject 7 invalid character = ZCZC-EAS-RWT-0
|
||||
reject 3 invalid character = ZCZC-EAS-RWT-012057-012080-012101-012103-01211
|
||||
reject 0 invalid character = ZCZC-EAS-RWT-012057-012081-012101-012103-012115+0030-2780415-W▒
|
||||
frame_buf 6 = ZCZC-EAS-RWT-012057-012081-012!01-012103-012115+0030-2780415-WTSP/TV-
|
||||
frame_buf 1 = ZCZC-EAS-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
|
||||
DECODED[2] 0:03.617 EAS audio level = 194(106/108) _|____|__
|
||||
[0.1] EAS>APDW16:{DEZCZC-EAS-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
|
||||
Case 3: Slice 6 is a mismatch (EAs vs. EAS).
|
||||
Slice 7 has RST rather than RWT.
|
||||
2 & 4 don't match either (012141 vs. 012101).
|
||||
We have another case where no two match so there is no clear winner.
|
||||
|
||||
|
||||
reject 5 invalid character = ZCZC-EAS-RWT-012057-012081-012101-012103-012115+▒
|
||||
frame_buf 7 = ZCZC-EAS-RST-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
frame_buf 6 = ZCZC-EAs-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
frame_buf 4 = ZCZC-EAS-RWT-112057-012081-012101-012103-012115+0030-2780415-WTSP/TV-
|
||||
frame_buf 2 = ZCZC-EAS-RWT-012057-012081-012141-012103-012115+0030-2780415-WTSP/TV-
|
||||
|
||||
DECODED[3] 0:05.920 EAS audio level = 194(106/108) __|_|_||_
|
||||
[0.2] EAS>APDW16:{DEZCZC-EAS-RWT-012057-012081-012141-012103-012115+0030-2780415-WTSP/TV-
|
||||
|
||||
Conclusions:
|
||||
|
||||
(1) The existing algorithm gives a higher preference to those frames matching others.
|
||||
We didn't see any cases here where that would be to our advantage.
|
||||
|
||||
(2) A partial solution would be more validity checking. (i.e. non-digit where
|
||||
digit is expected.) But wait... We might want to keep it for consideration:
|
||||
|
||||
(3) If I got REALLY ambitious, some day, we could compare all of them one column
|
||||
at a time and take the most popular (and valid for that column) character and
|
||||
use all of the most popular characters. Better yet, at the bit level.
|
||||
|
||||
Of course this is probably all overkill because we would normally expect to have pretty
|
||||
decent signals. The designers didn't even bother to add any sort of checksum for error checking.
|
||||
|
||||
The random errors injected are also not realistic. Actual noise would probably wipe out the
|
||||
same bit(s) for all of the slices.
|
||||
|
||||
The protocol specification suggests comparing all 3 transmissions and taking the best 2 out of 3.
|
||||
I think that would best be left to an external application and we just concentrate on being
|
||||
a good modem here and providing a result when it is received.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/***********************************************************************************
|
||||
*
|
||||
* Name: hdlc_rec_bit
|
||||
|
@ -238,11 +456,19 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
}
|
||||
}
|
||||
|
||||
// EAS does not use HDLC.
|
||||
|
||||
if (g_audio_p->achan[chan].modem_type == MODEM_EAS) {
|
||||
eas_rec_bit (chan, subchan, slice, raw, not_used_remove);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Different state information for each channel / subchannel / slice.
|
||||
*/
|
||||
H = &hdlc_state[chan][subchan][slice];
|
||||
|
||||
|
||||
/*
|
||||
* Using NRZI encoding,
|
||||
* A '0' bit is represented by an inversion since previous bit.
|
||||
|
@ -266,8 +492,11 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
}
|
||||
|
||||
// After BER insertion, NRZI, and any descrambling, feed into FX.25 decoder as well.
|
||||
// Don't waste time on this if AIS. EAS does not get this far.
|
||||
|
||||
fx25_rec_bit (chan, subchan, slice, dbit);
|
||||
if (g_audio_p->achan[chan].modem_type != MODEM_AIS) {
|
||||
fx25_rec_bit (chan, subchan, slice, dbit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Octets are sent LSB first.
|
||||
|
|
115
src/hdlc_rec2.c
115
src/hdlc_rec2.c
|
@ -123,6 +123,7 @@ static struct audio_s *save_audio_config_p;
|
|||
|
||||
#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2)
|
||||
|
||||
|
||||
/*
|
||||
* This is the current state of the HDLC decoder.
|
||||
*
|
||||
|
@ -132,7 +133,11 @@ static struct audio_s *save_audio_config_p;
|
|||
* Should have a reset function instead of initializations here.
|
||||
*/
|
||||
|
||||
struct hdlc_state_s {
|
||||
// TODO: Clean up. This is a remnant of splitting hdlc_rec.c into 2 parts.
|
||||
// This is not the same as hdlc_state_s in hdlc_rec.c
|
||||
// "2" was added to reduce confusion. Can be trimmed down.
|
||||
|
||||
struct hdlc_state2_s {
|
||||
|
||||
int prev_raw; /* Keep track of previous bit so */
|
||||
/* we can look for transitions. */
|
||||
|
@ -582,7 +587,7 @@ inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) {
|
|||
|
||||
static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall)
|
||||
{
|
||||
struct hdlc_state_s H;
|
||||
struct hdlc_state2_s H2;
|
||||
int blen; /* Block length in bits. */
|
||||
int i;
|
||||
int raw; /* From demodulator. Should be 0 or 1. */
|
||||
|
@ -594,10 +599,10 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
int retry_conf_retry = retry_conf.retry;
|
||||
|
||||
|
||||
H.is_scrambled = rrbb_get_is_scrambled (block);
|
||||
H.prev_descram = rrbb_get_prev_descram (block);
|
||||
H.lfsr = rrbb_get_descram_state (block);
|
||||
H.prev_raw = rrbb_get_bit (block, 0); /* Actually last bit of the */
|
||||
H2.is_scrambled = rrbb_get_is_scrambled (block);
|
||||
H2.prev_descram = rrbb_get_prev_descram (block);
|
||||
H2.lfsr = rrbb_get_descram_state (block);
|
||||
H2.prev_raw = rrbb_get_bit (block, 0); /* Actually last bit of the */
|
||||
/* opening flag so we can derive the */
|
||||
/* first data bit. */
|
||||
|
||||
|
@ -608,13 +613,13 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
|
||||
if ((retry_conf.mode == RETRY_MODE_CONTIGUOUS && is_contig_bit_modified(0, retry_conf)) ||
|
||||
(retry_conf.mode == RETRY_MODE_SEPARATED && is_sep_bit_modified(0, retry_conf))) {
|
||||
H.prev_raw = ! H.prev_raw;
|
||||
H2.prev_raw = ! H2.prev_raw;
|
||||
}
|
||||
|
||||
H.pat_det = 0;
|
||||
H.oacc = 0;
|
||||
H.olen = 0;
|
||||
H.frame_len = 0;
|
||||
H2.pat_det = 0;
|
||||
H2.oacc = 0;
|
||||
H2.olen = 0;
|
||||
H2.frame_len = 0;
|
||||
|
||||
blen = rrbb_get_len(block);
|
||||
|
||||
|
@ -646,49 +651,49 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
* Octets are sent LSB first.
|
||||
* Shift the most recent 8 bits thru the pattern detector.
|
||||
*/
|
||||
H.pat_det >>= 1;
|
||||
H2.pat_det >>= 1;
|
||||
|
||||
/*
|
||||
* Using NRZI encoding,
|
||||
* A '0' bit is represented by an inversion since previous bit.
|
||||
* A '1' bit is represented by no change.
|
||||
* Note: this code can be factorized with the raw != H.prev_raw code at the cost of processing time
|
||||
* Note: this code can be factorized with the raw != H2.prev_raw code at the cost of processing time
|
||||
*/
|
||||
|
||||
int dbit ;
|
||||
|
||||
if (H.is_scrambled) {
|
||||
if (H2.is_scrambled) {
|
||||
int descram;
|
||||
|
||||
descram = descramble(raw, &(H.lfsr));
|
||||
descram = descramble(raw, &(H2.lfsr));
|
||||
|
||||
dbit = (descram == H.prev_descram);
|
||||
H.prev_descram = descram;
|
||||
H.prev_raw = raw;
|
||||
dbit = (descram == H2.prev_descram);
|
||||
H2.prev_descram = descram;
|
||||
H2.prev_raw = raw;
|
||||
}
|
||||
else {
|
||||
|
||||
dbit = (raw == H.prev_raw);
|
||||
H.prev_raw = raw;
|
||||
dbit = (raw == H2.prev_raw);
|
||||
H2.prev_raw = raw;
|
||||
}
|
||||
|
||||
if (dbit) {
|
||||
|
||||
H.pat_det |= 0x80;
|
||||
H2.pat_det |= 0x80;
|
||||
/* Valid data will never have 7 one bits in a row: exit. */
|
||||
if (H.pat_det == 0xfe) {
|
||||
if (H2.pat_det == 0xfe) {
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("try_decode: found abort, i=%d\n", i);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
H.oacc >>= 1;
|
||||
H.oacc |= 0x80;
|
||||
H2.oacc >>= 1;
|
||||
H2.oacc |= 0x80;
|
||||
} else {
|
||||
|
||||
/* The special pattern 01111110 indicates beginning and ending of a frame: exit. */
|
||||
if (H.pat_det == 0x7e) {
|
||||
if (H2.pat_det == 0x7e) {
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("try_decode: found flag, i=%d\n", i);
|
||||
|
@ -703,10 +708,10 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
* "bit stuffing."
|
||||
*/
|
||||
|
||||
} else if ( (H.pat_det >> 2) == 0x1f ) {
|
||||
} else if ( (H2.pat_det >> 2) == 0x1f ) {
|
||||
continue;
|
||||
}
|
||||
H.oacc >>= 1;
|
||||
H2.oacc >>= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -714,14 +719,14 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
* into the frame buffer.
|
||||
*/
|
||||
|
||||
H.olen++;
|
||||
H2.olen++;
|
||||
|
||||
if (H.olen & 8) {
|
||||
H.olen = 0;
|
||||
if (H2.olen & 8) {
|
||||
H2.olen = 0;
|
||||
|
||||
if (H.frame_len < MAX_FRAME_LEN) {
|
||||
H.frame_buf[H.frame_len] = H.oacc;
|
||||
H.frame_len++;
|
||||
if (H2.frame_len < MAX_FRAME_LEN) {
|
||||
H2.frame_buf[H2.frame_len] = H2.oacc;
|
||||
H2.frame_len++;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -732,10 +737,10 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
|
||||
#if DEBUGx
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("try_decode: olen=%d, frame_len=%d\n", H.olen, H.frame_len);
|
||||
dw_printf ("try_decode: olen=%d, frame_len=%d\n", H2.olen, H2.frame_len);
|
||||
#endif
|
||||
|
||||
if (H.olen == 0 && H.frame_len >= MIN_FRAME_LEN) {
|
||||
if (H2.olen == 0 && H2.frame_len >= MIN_FRAME_LEN) {
|
||||
|
||||
unsigned short actual_fcs, expected_fcs;
|
||||
|
||||
|
@ -743,9 +748,9 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
if (retry_conf.type == RETRY_TYPE_NONE) {
|
||||
int j;
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("NEW WAY: frame len = %d\n", H.frame_len);
|
||||
for (j=0; j<H.frame_len; j++) {
|
||||
dw_printf (" %02x", H.frame_buf[j]);
|
||||
dw_printf ("NEW WAY: frame len = %d\n", H2.frame_len);
|
||||
for (j=0; j<H2.frame_len; j++) {
|
||||
dw_printf (" %02x", H2.frame_buf[j]);
|
||||
}
|
||||
dw_printf ("\n");
|
||||
|
||||
|
@ -760,15 +765,15 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
/* I think making a second pass over it and comparing is */
|
||||
/* easier to understand. */
|
||||
|
||||
actual_fcs = H.frame_buf[H.frame_len-2] | (H.frame_buf[H.frame_len-1] << 8);
|
||||
actual_fcs = H2.frame_buf[H2.frame_len-2] | (H2.frame_buf[H2.frame_len-1] << 8);
|
||||
|
||||
expected_fcs = fcs_calc (H.frame_buf, H.frame_len - 2);
|
||||
expected_fcs = fcs_calc (H2.frame_buf, H2.frame_len - 2);
|
||||
|
||||
if (actual_fcs == expected_fcs && save_audio_config_p->achan[chan].modem_type == MODEM_AIS) {
|
||||
|
||||
// Sanity check for AIS.
|
||||
if (ais_check_length((H.frame_buf[0] >> 2) & 0x3f, H.frame_len - 2) == 0) {
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry, 0); /* len-2 to remove FCS. */
|
||||
if (ais_check_length((H2.frame_buf[0] >> 2) & 0x3f, H2.frame_len - 2) == 0) {
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H2.frame_buf, H2.frame_len - 2, alevel, retry_conf.retry, 0); /* len-2 to remove FCS. */
|
||||
return 1; /* success */
|
||||
}
|
||||
else {
|
||||
|
@ -776,7 +781,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
}
|
||||
}
|
||||
else if (actual_fcs == expected_fcs &&
|
||||
sanity_check (H.frame_buf, H.frame_len - 2, retry_conf.retry, save_audio_config_p->achan[chan].sanity_test)) {
|
||||
sanity_check (H2.frame_buf, H2.frame_len - 2, retry_conf.retry, save_audio_config_p->achan[chan].sanity_test)) {
|
||||
|
||||
// TODO: Shouldn't be necessary to pass chan, subchan, alevel into
|
||||
// try_decode because we can obtain them from block.
|
||||
|
@ -784,7 +789,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
|
||||
assert (rrbb_get_chan(block) == chan);
|
||||
assert (rrbb_get_subchan(block) == subchan);
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, retry_conf.retry, 0); /* len-2 to remove FCS. */
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H2.frame_buf, H2.frame_len - 2, alevel, retry_conf.retry, 0); /* len-2 to remove FCS. */
|
||||
return 1; /* success */
|
||||
|
||||
} else if (passall) {
|
||||
|
@ -793,7 +798,7 @@ static int try_decode (rrbb_t block, int chan, int subchan, int slice, alevel_t
|
|||
//text_color_set(DW_COLOR_ERROR);
|
||||
//dw_printf ("ATTEMPTING PASSALL PROCESSING\n");
|
||||
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H.frame_buf, H.frame_len - 2, alevel, RETRY_MAX, 0); /* len-2 to remove FCS. */
|
||||
multi_modem_process_rec_frame (chan, subchan, slice, H2.frame_buf, H2.frame_len - 2, alevel, RETRY_MAX, 0); /* len-2 to remove FCS. */
|
||||
return 1; /* success */
|
||||
}
|
||||
else {
|
||||
|
@ -818,25 +823,25 @@ failure:
|
|||
text_color_set(DW_COLOR_ERROR);
|
||||
if (crc_failed)
|
||||
dw_printf ("CRC failed\n");
|
||||
if (H.olen != 0)
|
||||
dw_printf ("Bad olen: %d \n", H.olen);
|
||||
else if (H.frame_len < MIN_FRAME_LEN) {
|
||||
if (H2.olen != 0)
|
||||
dw_printf ("Bad olen: %d \n", H2.olen);
|
||||
else if (H2.frame_len < MIN_FRAME_LEN) {
|
||||
dw_printf ("Frame too small\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
dw_printf ("FAILURE with frame: frame len = %d\n", H.frame_len);
|
||||
dw_printf ("FAILURE with frame: frame len = %d\n", H2.frame_len);
|
||||
dw_printf ("\n");
|
||||
for (j=0; j<H.frame_len; j++) {
|
||||
dw_printf (" %02x", H.frame_buf[j]);
|
||||
for (j=0; j<H2.frame_len; j++) {
|
||||
dw_printf (" %02x", H2.frame_buf[j]);
|
||||
}
|
||||
dw_printf ("\nDEC\n");
|
||||
for (j=0; j<H.frame_len; j++) {
|
||||
dw_printf ("%c", H.frame_buf[j]>>1);
|
||||
for (j=0; j<H2.frame_len; j++) {
|
||||
dw_printf ("%c", H2.frame_buf[j]>>1);
|
||||
}
|
||||
dw_printf ("\nORIG\n");
|
||||
for (j=0; j<H.frame_len; j++) {
|
||||
dw_printf ("%c", H.frame_buf[j]);
|
||||
for (j=0; j<H2.frame_len; j++) {
|
||||
dw_printf ("%c", H2.frame_buf[j]);
|
||||
}
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
|
|
@ -322,7 +322,7 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
|
|||
assert (subchan >= 0 && subchan < MAX_SUBCHANS);
|
||||
assert (slice >= 0 && slice < MAX_SUBCHANS);
|
||||
|
||||
// Special encapsulation for AIS so it can be treated normally pretty much everywhere else.
|
||||
// Special encapsulation for AIS & EAS so they can be treated normally pretty much everywhere else.
|
||||
|
||||
if (save_audio_config_p->achan[chan].modem_type == MODEM_AIS) {
|
||||
char nmea[256];
|
||||
|
@ -334,10 +334,17 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
|
|||
|
||||
// alevel gets in there somehow making me question why it is passed thru here.
|
||||
}
|
||||
else if (save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
|
||||
char monfmt[300]; // EAS SAME message max length is 268
|
||||
|
||||
snprintf (monfmt, sizeof(monfmt), "EAS>%s%1d%1d:{%c%c%s", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, USER_DEF_USER_ID, USER_DEF_TYPE_EAS, fbuf);
|
||||
pp = ax25_from_text (monfmt, 1);
|
||||
|
||||
// alevel gets in there somehow making me question why it is passed thru here.
|
||||
}
|
||||
else {
|
||||
pp = ax25_from_frame (fbuf, flen, alevel);
|
||||
}
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Unexpected internal problem, %s %d\n", __FILE__, __LINE__);
|
||||
|
|
|
@ -18,3 +18,4 @@
|
|||
#define USER_DEF_USER_ID 'D' // user id D for direwolf
|
||||
|
||||
#define USER_DEF_TYPE_AIS 'A' // data type A for AIS NMEA sentence
|
||||
#define USER_DEF_TYPE_EAS 'E' // data type E for EAS broadcasts
|
||||
|
|
Loading…
Reference in New Issue