diff --git a/bands.json b/bands.json index 2f4fc98a..8fc6f7c8 100644 --- a/bands.json +++ b/bands.json @@ -367,5 +367,21 @@ "lower_bound": 446000000, "upper_bound": 446200000, "tags": ["public"] + }, + { + "name": "VHF Air", + "lower_bound": 108000000, + "upper_bound": 137000000, + "tags": ["service"] + }, + { + "name": "VHF Marine", + "lower_bound": 156000000, + "upper_bound": 174000000, + "frequencies": { + "ais": 161975000, + "ais": 162025000 + }, + "tags": ["service"] } ] diff --git a/csdr/chain/digimodes.py b/csdr/chain/digimodes.py index 7be5c4f2..50298fd6 100644 --- a/csdr/chain/digimodes.py +++ b/csdr/chain/digimodes.py @@ -43,6 +43,29 @@ class PacketDemodulator(ServiceDemodulator, DialFrequencyReceiver): self.parser.setDialFrequency(frequency) +class AisDemodulator(ServiceDemodulator, DialFrequencyReceiver): + def __init__(self, service: bool = False): + self.parser = AprsParser() + workers = [ + FmDemod(), + Convert(Format.FLOAT, Format.SHORT), + DirewolfModule(service=service, ais=True), + KissDeframer(), + Ax25Parser(), + self.parser, + ] + super().__init__(workers) + + def supportsSquelch(self) -> bool: + return False + + def getFixedAudioRate(self) -> int: + return 48000 + + def setDialFrequency(self, frequency: int) -> None: + self.parser.setDialFrequency(frequency) + + class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain): def __init__(self, baudRate: float): self.baudRate = baudRate diff --git a/owrx/aprs/module.py b/owrx/aprs/module.py index 28bb51df..b29c1e9c 100644 --- a/owrx/aprs/module.py +++ b/owrx/aprs/module.py @@ -14,10 +14,11 @@ logger = logging.getLogger(__name__) class DirewolfModule(AutoStartModule, DirewolfConfigSubscriber): - def __init__(self, service: bool = False): + def __init__(self, service: bool = False, ais: bool = False): self.process = None self.tcpSource = None self.service = service + self.ais = ais self.direwolfConfigPath = "{tmp_dir}/openwebrx_direwolf_{myid}.conf".format( tmp_dir=CoreConfig().get_temporary_directory(), myid=id(self) ) @@ -43,11 +44,14 @@ class DirewolfModule(AutoStartModule, DirewolfConfigSubscriber): file.close() # direwolf -c {direwolf_config} -r {audio_rate} -t 0 -q d -q h 1>&2 - self.process = Popen( - ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"], - start_new_session=True, - stdin=PIPE, - ) + cmdLine = ["direwolf", "-c", self.direwolfConfigPath, "-r", "48000", "-t", "0", "-q", "d", "-q", "h"] + + # for AIS mode, add -B AIS -A + if self.ais: + cmdLine += ["-B", "AIS", "-A"] + + # launch Direwolf + self.process = Popen(cmdLine, start_new_session=True, stdin=PIPE) # resume in case the reader has been stop()ed before self.reader.resume() diff --git a/owrx/dsp.py b/owrx/dsp.py index adef6a25..bf386f21 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -604,6 +604,9 @@ class DspManager(SdrSourceEventClient, ClientDemodulatorSecondaryDspEventClient) elif mod == "packet": from csdr.chain.digimodes import PacketDemodulator return PacketDemodulator() + elif mod == "ais": + from csdr.chain.digimodes import AisDemodulator + return AisDemodulator() elif mod == "pocsag": from csdr.chain.digiham import PocsagDemodulator return PocsagDemodulator() diff --git a/owrx/modes.py b/owrx/modes.py index c8b22061..66812e1f 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -130,6 +130,15 @@ class Modes(object): service=True, squelch=False, ), + DigitalMode( + "ais", + "AIS", + underlying=["nfm"], + bandpass=Bandpass(-6250, 6250), + requirements=["packet"], + service=True, + squelch=False, + ), DigitalMode( "pocsag", "Pocsag", diff --git a/owrx/service/__init__.py b/owrx/service/__init__.py index ffe2d9bf..b3c5e4f0 100644 --- a/owrx/service/__init__.py +++ b/owrx/service/__init__.py @@ -291,6 +291,9 @@ class ServiceHandler(SdrSourceEventClient): elif mod == "packet": from csdr.chain.digimodes import PacketDemodulator return PacketDemodulator(service=True) + elif mod == "ais": + from csdr.chain.digimodes import AisDemodulator + return AisDemodulator(service=True) elif mod == "sstv": from csdr.chain.digimodes import SstvDemodulator return SstvDemodulator(service=True)