restore PSK decoding

This commit is contained in:
Jakob Ketterl
2021-09-23 18:43:41 +02:00
parent 3fa3aac766
commit cbcba5807f
6 changed files with 145 additions and 51 deletions

View File

@ -1,21 +1,8 @@
from csdr.chain import Chain
from abc import ABC, abstractmethod
from abc import ABC, ABCMeta, abstractmethod
from pycsdr.modules import Writer
class BaseDemodulatorChain(Chain):
def supportsSquelch(self) -> bool:
return True
def setSampleRate(self, sampleRate: int) -> None:
pass
class SecondaryDemodulator(Chain):
def supportsSquelch(self) -> bool:
return True
class FixedAudioRateChain(ABC):
@abstractmethod
def getFixedAudioRate(self) -> int:
@ -49,3 +36,28 @@ class SlotFilterChain(ABC):
@abstractmethod
def setSlotFilter(self, filter: int) -> None:
pass
class SecondarySelectorChain(ABC):
def getBandwidth(self) -> float:
pass
class BaseDemodulatorChain(Chain):
def supportsSquelch(self) -> bool:
return True
def setSampleRate(self, sampleRate: int) -> None:
pass
class SecondaryDemodulator(Chain):
def supportsSquelch(self) -> bool:
return True
def setSampleRate(self, sampleRate: int) -> None:
pass
class ServiceDemodulator(SecondaryDemodulator, FixedAudioRateChain, metaclass=ABCMeta):
pass

View File

@ -1,15 +1,15 @@
from csdr.chain.demodulator import SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver
from csdr.chain.demodulator import ServiceDemodulator, SecondaryDemodulator, DialFrequencyReceiver, SecondarySelectorChain
from owrx.audio.chopper import AudioChopper
from owrx.aprs.kiss import KissDeframer
from owrx.aprs import Ax25Parser, AprsParser
from pycsdr.modules import Convert, FmDemod
from pycsdr.modules import Convert, FmDemod, Agc, TimingRecovery, DBPskDecoder, VaricodeDecoder
from pycsdr.types import Format
from owrx.aprs.module import DirewolfModule
from digiham.modules import FskDemodulator, PocsagDecoder
from owrx.pocsag import PocsagParser
class AudioChopperDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver):
class AudioChopperDemodulator(ServiceDemodulator, DialFrequencyReceiver):
# TODO parser typing
def __init__(self, mode: str, parser):
self.chopper = AudioChopper(mode, parser)
@ -23,7 +23,7 @@ class AudioChopperDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFre
self.chopper.setDialFrequency(frequency)
class PacketDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequencyReceiver):
class PacketDemodulator(ServiceDemodulator, DialFrequencyReceiver):
def __init__(self, service: bool = False):
self.parser = AprsParser()
workers = [
@ -46,7 +46,7 @@ class PacketDemodulator(SecondaryDemodulator, FixedAudioRateChain, DialFrequency
self.parser.setDialFrequency(frequency)
class PocsagDemodulator(SecondaryDemodulator, FixedAudioRateChain):
class PocsagDemodulator(ServiceDemodulator):
def __init__(self):
workers = [
FmDemod(),
@ -61,3 +61,28 @@ class PocsagDemodulator(SecondaryDemodulator, FixedAudioRateChain):
def getFixedAudioRate(self) -> int:
return 48000
class PskDemodulator(SecondaryDemodulator, SecondarySelectorChain):
def __init__(self, baudRate: float):
self.baudRate = baudRate
# this is an assumption, we will adjust in setSampleRate
self.sampleRate = 12000
secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3
workers = [
Agc(Format.COMPLEX_FLOAT),
TimingRecovery(secondary_samples_per_bits, 0.5, 2, useQ=True),
DBPskDecoder(),
VaricodeDecoder(),
]
super().__init__(workers)
def getBandwidth(self):
return self.baudRate
def setSampleRate(self, sampleRate: int) -> None:
if sampleRate == self.sampleRate:
return
self.sampleRate = sampleRate
secondary_samples_per_bits = int(round(self.sampleRate / self.baudRate)) & ~3
self.replace(1, TimingRecovery(secondary_samples_per_bits, 0.5, 2, useQ=True))

View File

@ -62,10 +62,12 @@ class Decimator(Chain):
class Selector(Chain):
def __init__(self, inputRate: int, outputRate: int, shiftRate: float, withSquelch: bool = True):
def __init__(self, inputRate: int, outputRate: int, withSquelch: bool = True):
self.inputRate = inputRate
self.outputRate = outputRate
self.frequencyOffset = 0
self.shift = Shift(shiftRate)
self.shift = Shift(0.0)
self.decimation = Decimator(inputRate, outputRate)
@ -88,8 +90,15 @@ class Selector(Chain):
bp_transition = 320.0 / self.outputRate
return Bandpass(transition=bp_transition, use_fft=True)
def setShiftRate(self, rate: float) -> None:
self.shift.setRate(rate)
def setFrequencyOffset(self, offset: int) -> None:
if offset == self.frequencyOffset:
return
self.frequencyOffset = offset
self._updateShift()
def _updateShift(self):
shift = -self.frequencyOffset / self.inputRate
self.shift.setRate(shift)
def _convertToLinear(self, db: float) -> float:
return float(math.pow(10, db / 10))
@ -125,4 +134,27 @@ class Selector(Chain):
self.replace(2, self.bandpass)
def setInputRate(self, inputRate: int) -> None:
if inputRate == self.inputRate:
return
self.inputRate = inputRate
self.decimation.setInputRate(inputRate)
self._updateShift()
class SecondarySelector(Chain):
def __init__(self, sampleRate: int, bandwidth: float):
self.sampleRate = sampleRate
self.frequencyOffset = 0
self.shift = Shift(0.0)
cutoffRate = bandwidth / sampleRate
self.bandpass = Bandpass(-cutoffRate, cutoffRate, cutoffRate, use_fft=True)
workers = [self.shift, self.bandpass]
super().__init__(workers)
def setFrequencyOffset(self, offset: int) -> None:
if offset == self.frequencyOffset:
return
self.frequencyOffset = offset
if self.frequencyOffset is None:
return
self.shift.setRate(-offset / self.sampleRate)