Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JT4 enablement #374

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Added support for the MSK144 digimode
- Added support for decoding ADS-B with dump1090
- Added support for decoding HFDL and VDL2 aircraft communications
- Added support for decoding JT4 modes
- Added decoding of ISM band transmissions using rtl_433
- Added support for decoding RDS data on WFM broadcasts using redsea decoder
- Added decoding for DAB broadcast stations using csdr-eti and dablin
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ It has the following features:
- Multiple SDR devices can be used simultaneously
- [digiham](https://github.com/jketterl/digiham) based demodularors (DMR, YSF, Pocsag, D-Star, NXDN)
- [wsjt-x](https://wsjt.sourceforge.io/) based demodulators (FT8, FT4, WSPR, JT65, JT9, FST4,
FST4W)
FST4W, JT4)
- [direwolf](https://github.com/wb2osz/direwolf) based demodulation of APRS packets
- [JS8Call](http://js8call.com/) support
- [DRM](https://github.com/jketterl/openwebrx/wiki/DRM-demodulator-notes) support
Expand Down
1 change: 1 addition & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ openwebrx (1.3.0) UNRELEASED; urgency=low
* Added support for the MSK144 digimode
* Added support for decoding ADS-B with dump1090
* Added support for decoding HFDL and VDL2 aircraft communications
* Added support for decoding JT4 modes
* Added decoding of ISM band transmissions using rtl_433
* Added support for decoding RDS data on WFM broadcasts using redsea decoder
* Added decoding for DAB broadcast stations using csdr-eti and dablin
Expand Down
4 changes: 2 additions & 2 deletions htdocs/lib/DemodulatorPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ DemodulatorPanel.prototype.updatePanels = function() {
var mode = Modes.findByModulation(modulation);
toggle_panel("openwebrx-panel-digimodes", modulation && (!mode || mode.secondaryFft));
// WSJT-X modes share the same panel
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'ft4', 'fst4', 'fst4w', "q65", "msk144"].indexOf(modulation) >= 0);
toggle_panel("openwebrx-panel-wsjt-message", ['ft8', 'wspr', 'jt65', 'jt9', 'jt4', 'ft4', 'fst4', 'fst4w', "q65", "msk144"].indexOf(modulation) >= 0);
// these modes come with their own
['js8', 'packet', 'pocsag', 'adsb', 'ism', 'hfdl', 'vdl2'].forEach(function(m) {
toggle_panel('openwebrx-panel-' + m + '-message', modulation === m);
Expand Down Expand Up @@ -382,4 +382,4 @@ $.fn.demodulatorPanel = function(){
this.data('panel', new DemodulatorPanel(this));
}
return this.data('panel');
};
};
4 changes: 2 additions & 2 deletions htdocs/lib/MessagePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ MessagePanel.prototype.scrollToBottom = function() {
function WsjtMessagePanel(el) {
MessagePanel.call(this, el);
this.initClearTimer();
this.qsoModes = ['FT8', 'JT65', 'JT9', 'FT4', 'FST4', 'Q65', 'MSK144'];
this.qsoModes = ['FT8', 'JT65', 'JT9', 'JT4', 'FT4', 'FST4', 'Q65', 'MSK144'];
this.beaconModes = ['WSPR', 'FST4W'];
this.modes = [].concat(this.qsoModes, this.beaconModes);
}
Expand Down Expand Up @@ -809,4 +809,4 @@ $.fn.vdl2MessagePanel = function() {
this.data('panel', new Vdl2MessagePanel(this));
}
return this.data('panel');
};
};
1 change: 1 addition & 0 deletions owrx/config/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
wsjt_decoding_depths=PropertyLayer(jt65=1),
fst4_enabled_intervals=[15, 30],
fst4w_enabled_intervals=[120, 300],
jt4_enabled_submodes=["F", "G"],
q65_enabled_combinations=["A30", "E120", "C60"],
js8_enabled_profiles=["normal", "slow"],
js8_decoding_depth=3,
Expand Down
7 changes: 6 additions & 1 deletion owrx/controllers/settings/decoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from owrx.form.input.wfm import WfmTauValues
from owrx.form.input.wsjt import Q65ModeMatrix, WsjtDecodingDepthsInput
from owrx.form.input.converter import OptionalConverter
from owrx.wsjt import Fst4Profile, Fst4wProfile
from owrx.wsjt import Fst4Profile, Fst4wProfile, JT4Profile
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem


Expand Down Expand Up @@ -90,6 +90,11 @@ def getSections(self):
"Enabled FST4W intervals",
[Option(v, "{}s".format(v)) for v in Fst4wProfile.availableIntervals],
),
MultiCheckboxInput(
"jt4_enabled_submodes",
"Enabled JT4 Submodes",
[Option(v, "{}".format(v)) for v in JT4Profile.availableSubmodes],
),
Q65ModeMatrix("q65_enabled_combinations", "Enabled Q65 Mode combinations"),
),
]
2 changes: 1 addition & 1 deletion owrx/dsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ def setDemodulator(self, mod):
def _getSecondaryDemodulator(self, mod) -> Optional[SecondaryDemodulator]:
if isinstance(mod, SecondaryDemodulator):
return mod
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
if mod in ["ft8", "wspr", "jt65", "jt9", "jt4", "ft4", "fst4", "fst4w", "q65"]:
from csdr.chain.digimodes import AudioChopperDemodulator
from owrx.wsjt import WsjtParser
return AudioChopperDemodulator(mod, WsjtParser())
Expand Down
1 change: 1 addition & 0 deletions owrx/modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class Modes(object):
WsjtMode("wspr", "WSPR", bandpass=Bandpass(1350, 1650)),
WsjtMode("fst4", "FST4", requirements=["wsjt-x-2-3"]),
WsjtMode("fst4w", "FST4W", bandpass=Bandpass(1350, 1650), requirements=["wsjt-x-2-3"]),
WsjtMode("jt4", "JT4"),
WsjtMode("q65", "Q65", requirements=["wsjt-x-2-4"]),
DigitalMode("msk144", "MSK144", requirements=["msk144"], underlying=["usb"], service=True),
Js8Mode("js8", "JS8Call"),
Expand Down
2 changes: 1 addition & 1 deletion owrx/reporting/pskreporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def getSupportedModes(self):
Current version at the time of the last change:
https://www.adif.org/314/ADIF_314.htm#Mode_Enumeration
"""
return ["FT8", "FT4", "JT9", "JT65", "FST4", "JS8", "Q65", "WSPR", "FST4W", "MSK144"]
return ["FT8", "FT4", "JT9", "JT4", "JT65", "FST4", "JS8", "Q65", "WSPR", "FST4W", "MSK144"]

def stop(self):
self.cancelTimer()
Expand Down
2 changes: 1 addition & 1 deletion owrx/service/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def _getDemodulator(self, demod: Union[str, BaseDemodulatorChain]):
def _getSecondaryDemodulator(self, mod) -> Optional[ServiceDemodulator]:
if isinstance(mod, ServiceDemodulatorChain):
return mod
if mod in ["ft8", "wspr", "jt65", "jt9", "ft4", "fst4", "fst4w", "q65"]:
if mod in ["ft8", "wspr", "jt65", "jt9", "jt4", "ft4", "fst4", "fst4w", "q65"]:
from csdr.chain.digimodes import AudioChopperDemodulator
from owrx.wsjt import WsjtParser
return AudioChopperDemodulator(mod, WsjtParser())
Expand Down
31 changes: 31 additions & 0 deletions owrx/wsjt.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ def getProfiles(self) -> List[AudioChopperProfile]:
return [Fst4wProfile(i) for i in profiles if i in Fst4wProfile.availableIntervals]


class JT4ProfileSource(ConfigWiredProfileSource):
def getPropertiesToWire(self) -> List[str]:
return ["jt4_enabled_submodes"]

def getProfiles(self) -> List[AudioChopperProfile]:
config = Config.get()
profiles = config["jt4_enabled_submodes"] if "jt4_enabled_submodes" in config else []
return [JT4Profile(i) for i in profiles if i in JT4Profile.availableSubmodes]


class Q65ProfileSource(ConfigWiredProfileSource):
def getPropertiesToWire(self) -> List[str]:
return ["q65_enabled_combinations"]
Expand Down Expand Up @@ -102,6 +112,8 @@ def getSource(mode: str):
return Fst4ProfileSource()
elif mode == "fst4w":
return Fst4wProfileSource()
elif mode == "jt4":
return JT4ProfileSource()
elif mode == "q65":
return Q65ProfileSource()

Expand Down Expand Up @@ -197,6 +209,25 @@ def getMode(self):
return "FST4W"


class JT4Profile(WsjtProfile):
availableSubmodes = ["A", "B", "C", "D", "E", "F", "G"]

def __init__(self, submode):
self.submode = submode

def getInterval(self):
return 60

def getSubmode(self):
sq6emm marked this conversation as resolved.
Show resolved Hide resolved
return self.submode

def decoder_commandline(self, file):
return ["jt9", "-4", "-b", str(self.submode), "-d", str(self.decoding_depth()), file]

def getMode(self):
return "JT4"


class Q65Mode(Enum):
# value is the bandwidth multiplier according to https://physics.princeton.edu/pulsar/k1jt/Q65_Quick_Start.pdf
A = 1
Expand Down