Pārlūkot izejas kodu

Support audio-based decoders and document tools

master
Jan Svabenik pirms 3 dienas
vecāks
revīzija
d0ed1c4d94
8 mainītis faili ar 45 papildinājumiem un 39 dzēšanām
  1. +6
    -0
      .gitignore
  2. +1
    -1
      README.md
  3. +5
    -1
      cmd/sdrd/main.go
  4. +2
    -2
      config.yaml
  5. +11
    -30
      internal/decoder/decoder.go
  6. +7
    -1
      internal/recorder/decode.go
  7. +2
    -2
      internal/recorder/decode_on_demand.go
  8. +11
    -2
      tools/README.md

+ 6
- 0
.gitignore Parādīt failu

@@ -24,5 +24,11 @@ web/openai/
web/*-web.zip
sdr-visual-suite.zip

# downloaded decoder binaries
tools/downloads/
tools/wsjtx/
tools/fldigi/
tools/dsd-neo/

# temp binaries
sdrd.exe~

+ 1
- 1
README.md Parādīt failu

@@ -67,7 +67,7 @@ Edit `config.yaml`:
- `detector.threshold_db`: power threshold in dB
- `detector.min_duration_ms`, `detector.hold_ms`: debounce/merge
- `recorder.*`: enable IQ/audio recording, preroll, output_dir, max_disk_mb
- `decoder.*`: external decode commands (use `{iq}` and `{sr}` placeholders)
- `decoder.*`: external decode commands (use `{iq}`, `{audio}`, `{sr}` placeholders)

## APIs
### Config API


+ 5
- 1
cmd/sdrd/main.go Parādīt failu

@@ -570,7 +570,11 @@ func main() {
http.Error(w, "meta read failed", http.StatusInternalServerError)
return
}
res, err := recorder.DecodeOnDemand(cmd, filepath.Join(base, "signal.cf32"), meta.SampleRate)
audioPath := filepath.Join(base, "audio.wav")
if _, errStat := os.Stat(audioPath); errStat != nil {
audioPath = ""
}
res, err := recorder.DecodeOnDemand(cmd, filepath.Join(base, "signal.cf32"), meta.SampleRate, audioPath)
if err != nil {
http.Error(w, res.Stderr, http.StatusInternalServerError)
return


+ 2
- 2
config.yaml Parādīt failu

@@ -32,8 +32,8 @@ recorder:
decoder:
ft8_cmd: "tools/ft8/ft8_decoder --iq {iq} --sample-rate {sr}"
wspr_cmd: "tools/wspr/wspr_decoder --iq {iq} --sample-rate {sr}"
dmr_cmd: "tools/dmr/dmr_decoder --iq {iq} --sample-rate {sr}"
dstar_cmd: "tools/dstar/dstar_decoder --iq {iq} --sample-rate {sr}"
dmr_cmd: "tools/dsd-neo/bin/dsd-neo.exe -fs -i {audio} -s {sr} -o null"
dstar_cmd: "tools/dsd-neo/bin/dsd-neo.exe -fd -i {audio} -s {sr} -o null"
fsk_cmd: "tools/fsk/fsk_decoder --iq {iq} --sample-rate {sr}"
psk_cmd: "tools/psk/psk_decoder --iq {iq} --sample-rate {sr}"
web_addr: ":8080"


+ 11
- 30
internal/decoder/decoder.go Parādīt failu

@@ -3,6 +3,7 @@ package decoder
import (
"errors"
"os/exec"
"strconv"
"strings"
)

@@ -12,14 +13,20 @@ type Result struct {
Code int `json:"code"`
}

// Run executes an external decoder command. If cmdTemplate contains {iq} or {sr}, they are replaced.
// Run executes an external decoder command. If cmdTemplate contains {iq}/{sr}/{audio}, they are replaced.
// Otherwise, --iq and --sample-rate args are appended.
func Run(cmdTemplate string, iqPath string, sampleRate int) (Result, error) {
func Run(cmdTemplate string, iqPath string, sampleRate int, audioPath string) (Result, error) {
if cmdTemplate == "" {
return Result{}, errors.New("decoder command empty")
}
cmdStr := strings.ReplaceAll(cmdTemplate, "{iq}", iqPath)
cmdStr = strings.ReplaceAll(cmdStr, "{sr}", intToString(sampleRate))
cmdStr = strings.ReplaceAll(cmdStr, "{sr}", strconv.Itoa(sampleRate))
if strings.Contains(cmdTemplate, "{audio}") {
if audioPath == "" {
return Result{}, errors.New("audio path required for decoder")
}
cmdStr = strings.ReplaceAll(cmdStr, "{audio}", audioPath)
}
parts := strings.Fields(cmdStr)
if len(parts) == 0 {
return Result{}, errors.New("invalid decoder command")
@@ -29,7 +36,7 @@ func Run(cmdTemplate string, iqPath string, sampleRate int) (Result, error) {
cmd.Args = append(cmd.Args, "--iq", iqPath)
}
if !strings.Contains(cmdTemplate, "{sr}") {
cmd.Args = append(cmd.Args, "--sample-rate", intToString(sampleRate))
cmd.Args = append(cmd.Args, "--sample-rate", strconv.Itoa(sampleRate))
}
out, err := cmd.CombinedOutput()
res := Result{Stdout: string(out), Code: 0}
@@ -40,29 +47,3 @@ func Run(cmdTemplate string, iqPath string, sampleRate int) (Result, error) {
}
return res, nil
}

func intToString(v int) string {
return strings.TrimSpace(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(strings.TrimSpace(strings.TrimSpace((func() string { return string(rune('0')) })()))), ""), ""), "")) + itoa(v)
}

func itoa(v int) string {
if v == 0 {
return "0"
}
n := v
if n < 0 {
n = -n
}
buf := make([]byte, 0, 12)
for n > 0 {
buf = append(buf, byte('0'+n%10))
n /= 10
}
if v < 0 {
buf = append(buf, '-')
}
for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
buf[i], buf[j] = buf[j], buf[i]
}
return string(buf)
}

+ 7
- 1
internal/recorder/decode.go Parādīt failu

@@ -18,7 +18,13 @@ func (m *Manager) runDecodeIfConfigured(mod string, iqPath string, sampleRate in
if cmd == "" {
return
}
res, err := decoder.Run(cmd, iqPath, sampleRate)
audioPath := ""
if v, ok := files["audio"]; ok {
if name, ok := v.(string); ok {
audioPath = filepath.Join(dir, name)
}
}
res, err := decoder.Run(cmd, iqPath, sampleRate, audioPath)
if err != nil {
return
}


+ 2
- 2
internal/recorder/decode_on_demand.go Parādīt failu

@@ -6,9 +6,9 @@ import (
"sdr-visual-suite/internal/decoder"
)

func DecodeOnDemand(cmd string, iqPath string, sampleRate int) (decoder.Result, error) {
func DecodeOnDemand(cmd string, iqPath string, sampleRate int, audioPath string) (decoder.Result, error) {
if cmd == "" {
return decoder.Result{}, errors.New("decoder command empty")
}
return decoder.Run(cmd, iqPath, sampleRate)
return decoder.Run(cmd, iqPath, sampleRate, audioPath)
}

+ 11
- 2
tools/README.md Parādīt failu

@@ -10,9 +10,18 @@ Examples (Windows):
- `tools/fsk/fsk_decoder.bat`
- `tools/psk/psk_decoder.bat`

Each script should accept:
Each script should accept either IQ or audio:
```
--iq <path> --sample-rate <sr>
```
Or:
```
--audio <path> --sample-rate <sr>
```

The app replaces `{iq}`, `{sr}`, and `{audio}` placeholders in config commands.

The app replaces `{iq}` and `{sr}` placeholders in config commands.
Downloaded:
- WSJT-X installer: tools/wsjtx/wsjtx-2.7.0-win64.exe
- fldigi installer: tools/fldigi/fldigi-latest.exe
- dsd-neo binary: tools/dsd-neo/bin/dsd-neo.exe

Notiek ielāde…
Atcelt
Saglabāt