kopia lustrzana https://github.com/f4exb/sdrangel
FileRecord improvement: finalized the command to rescue corrupted .sdriq files
rodzic
f88dcd01e6
commit
f6b1fd252e
|
@ -13,6 +13,7 @@ sdrangelove.supp
|
|||
*.cs
|
||||
*.pro.user
|
||||
.idea/*
|
||||
rescuesdriq/rescuesdriq
|
||||
debian/sdrangel/*
|
||||
debian/sdrangel.substvars
|
||||
debian/files
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<h1>Repair or reformat record (.sdriq) files</h1>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
This utility attempts to repair .sdriq files that have their header corrupted or with a pre version 4.2.1 header. Since version 4.2.1 a CRC32 checksum is present and the file will not be played if the check of the header content against the CRC32 fails.
|
||||
|
||||
The header is composed as follows:
|
||||
|
||||
- Sample rate in S/s (4 bytes, 32 bits)
|
||||
- Center frequency in Hz (8 bytes, 64 bits)
|
||||
- Start time Unix timestamp epoch in seconds (8 bytes, 64 bits)
|
||||
- Sample size as 16 or 24 bits (4 bytes, 32 bits)
|
||||
- filler with all zeroes (4 bytes, 32 bits)
|
||||
- CRC32 (IEEE) of the 28 bytes above (4 bytes, 32 bits)
|
||||
|
||||
The header size is 32 bytes in total which is a multiple of 8 bytes thus occupies an integer number of samples whether in 16 or 24 bits mode. When migrating from a pre version 4.2.1 header you may crunch a very small amount of samples.
|
||||
|
||||
You can replace values in the header with the following options:
|
||||
|
||||
- -sr uint
|
||||
Sample rate (S/s)
|
||||
- -cf uint
|
||||
Center frequency (Hz)
|
||||
- -ts string
|
||||
start time RFC3339 (ex: 2006-01-02T15:04:05Z)
|
||||
- -now
|
||||
use now for start time
|
||||
- -sz uint
|
||||
Sample size (16 or 24) (default 16)
|
||||
|
||||
You need to specify an input file. If no output file is specified the current header values are printed to the console and the program exits:
|
||||
|
||||
- -in string
|
||||
input file (default "foo")
|
||||
|
||||
To convert to a new file you need to specify the output file:
|
||||
|
||||
- -out string
|
||||
output file (default "foo")
|
||||
|
||||
You can specify a block size in multiples of 4k for the copy. Large blocks will yield a faster copy but a larger output file. With the default of 1 (4k) the copy does not take much time anyway:
|
||||
|
||||
- -bz uint
|
||||
Copy block size in multiple of 4k (default 1)
|
||||
|
||||
<h2>Build</h2>
|
||||
|
||||
The program is written in go and is provided only in source code form. Compiling it is very easy:
|
||||
|
||||
<h3>Install go</h3>
|
||||
|
||||
You will usually find a `golang` package in your distribution. For example in Ubuntu or Debian you can install it with `sudo apt-get install golang`. You can find binary distributions for many systems at the [Go download site](https://golang.org/dl/)
|
||||
|
||||
<h3>Build the program</h3>
|
||||
|
||||
In this directory just do `go build`
|
||||
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
|
||||
|
@ -17,27 +18,18 @@ type HeaderStd struct {
|
|||
CenterFrequency uint64
|
||||
StartTimestamp int64
|
||||
SampleSize uint32
|
||||
_ uint32
|
||||
Filler uint32
|
||||
CRC32 uint32
|
||||
}
|
||||
|
||||
func analyze(fileName string) HeaderStd {
|
||||
fmt.Println("input file:", fileName)
|
||||
|
||||
// open input file
|
||||
fi, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
// close fi on exit and check for its returned error
|
||||
defer func() {
|
||||
if err := fi.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
// make a read buffer
|
||||
r := bufio.NewReader(fi)
|
||||
|
||||
}
|
||||
|
||||
func analyze(r *bufio.Reader) HeaderStd {
|
||||
headerbuf := make([]byte, 32) // This is a full header with CRC
|
||||
n, err := r.Read(headerbuf)
|
||||
if err != nil && err != io.EOF {
|
||||
|
@ -50,27 +42,138 @@ func analyze(fileName string) HeaderStd {
|
|||
var header HeaderStd
|
||||
headerr := bytes.NewReader(headerbuf)
|
||||
err = binary.Read(headerr, binary.LittleEndian, &header)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Sample rate:", header.SampleRate)
|
||||
fmt.Println("Frequency :", header.CenterFrequency)
|
||||
fmt.Println("Sample Size:", header.SampleSize)
|
||||
tm := time.Unix(header.StartTimestamp, 0)
|
||||
fmt.Println("Start :", tm)
|
||||
check(err)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func writeHeader(writer *bufio.Writer, header *HeaderStd) {
|
||||
var bin_buf bytes.Buffer
|
||||
binary.Write(&bin_buf, binary.LittleEndian, header)
|
||||
noh, err := writer.Write(bin_buf.Bytes())
|
||||
check(err)
|
||||
fmt.Printf("Wrote %d bytes header\n", noh)
|
||||
}
|
||||
|
||||
func printHeader(header *HeaderStd) {
|
||||
fmt.Println("Sample rate:", header.SampleRate)
|
||||
fmt.Println("Frequency :", header.CenterFrequency)
|
||||
fmt.Println("Sample Size:", header.SampleSize)
|
||||
tm := time.Unix(header.StartTimestamp, 0)
|
||||
fmt.Println("Start :", tm)
|
||||
}
|
||||
|
||||
func setCRC(header *HeaderStd) {
|
||||
var bin_buf bytes.Buffer
|
||||
header.Filler = 0
|
||||
binary.Write(&bin_buf, binary.LittleEndian, header)
|
||||
header.CRC32 = crc32.ChecksumIEEE(bin_buf.Bytes()[0:28])
|
||||
}
|
||||
|
||||
func copyContent(reader *bufio.Reader, writer *bufio.Writer, blockSize uint) {
|
||||
p := make([]byte, blockSize*4096) // buffer in 4k multiples
|
||||
var sz int64 = 0
|
||||
|
||||
for {
|
||||
n, err := reader.Read(p)
|
||||
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
writer.Write(p[0:n])
|
||||
sz += int64(n)
|
||||
break
|
||||
} else {
|
||||
fmt.Println("An error occured during content copy. Aborting")
|
||||
break
|
||||
}
|
||||
} else {
|
||||
writer.Write(p)
|
||||
sz += int64(blockSize)*4096
|
||||
}
|
||||
|
||||
fmt.Printf("Wrote %d bytes\r", sz)
|
||||
}
|
||||
|
||||
fmt.Printf("Wrote %d bytes\r", sz)
|
||||
}
|
||||
|
||||
func main() {
|
||||
wordPtr := flag.String("in", "foo", "input file")
|
||||
inFileStr := flag.String("in", "foo", "input file")
|
||||
outFileStr := flag.String("out", "foo", "output file")
|
||||
sampleRate := flag.Uint("sr", 0, "Sample rate (S/s)")
|
||||
centerFreq := flag.Uint64("cf", 0, "Center frequency (Hz)")
|
||||
sampleSize := flag.Uint("sz", 16, "Sample size (16 or 24)")
|
||||
timeStr := flag.String("ts", "", "start time RFC3339 (ex: 2006-01-02T15:04:05Z)")
|
||||
timeNow := flag.Bool("now", false , "use now for start time")
|
||||
blockSize := flag.Uint("bz", 1, "Copy block size in multiple of 4k")
|
||||
|
||||
flag.Parse()
|
||||
flagSeen := make(map[string]bool)
|
||||
flag.Visit(func(f *flag.Flag) { flagSeen[f.Name] = true })
|
||||
|
||||
if flagSeen["in"] {
|
||||
analyze(*wordPtr)
|
||||
fmt.Println("input file :", *inFileStr)
|
||||
|
||||
// open input file
|
||||
fi, err := os.Open(*inFileStr)
|
||||
check(err)
|
||||
// close fi on exit and check for its returned error
|
||||
defer func() {
|
||||
err := fi.Close();
|
||||
check(err)
|
||||
}()
|
||||
// make a read buffer
|
||||
reader := bufio.NewReader(fi)
|
||||
var headerOrigin HeaderStd = analyze(reader)
|
||||
printHeader(&headerOrigin)
|
||||
|
||||
if flagSeen["out"] {
|
||||
if flagSeen["sr"] {
|
||||
headerOrigin.SampleRate = uint32(*sampleRate)
|
||||
} else if flagSeen["cf"] {
|
||||
headerOrigin.CenterFrequency = *centerFreq
|
||||
} else if flagSeen["sz"] {
|
||||
if (*sampleSize == 16) || (*sampleSize == 24) {
|
||||
headerOrigin.SampleSize = uint32(*sampleSize)
|
||||
} else {
|
||||
fmt.Println("Incorrect sample size specified. Defaulting to 16")
|
||||
headerOrigin.SampleSize = 16
|
||||
}
|
||||
} else if flagSeen["ts"] {
|
||||
t, err := time.Parse(time.RFC3339, *timeStr)
|
||||
if (err == nil) {
|
||||
headerOrigin.StartTimestamp = t.Unix()
|
||||
} else {
|
||||
fmt.Println("Incorrect time specified. Defaulting to now")
|
||||
headerOrigin.StartTimestamp = int64(time.Now().Unix())
|
||||
}
|
||||
} else if *timeNow {
|
||||
headerOrigin.StartTimestamp = int64(time.Now().Unix())
|
||||
}
|
||||
|
||||
fmt.Println("\nHeader is now")
|
||||
printHeader(&headerOrigin)
|
||||
setCRC(&headerOrigin)
|
||||
fmt.Println("CRC32 :", headerOrigin.CRC32)
|
||||
fmt.Println("Output file:", *outFileStr)
|
||||
|
||||
fo, err := os.Create(*outFileStr)
|
||||
check(err)
|
||||
|
||||
defer func() {
|
||||
err := fo.Close();
|
||||
check(err)
|
||||
}()
|
||||
|
||||
writer := bufio.NewWriter(fo)
|
||||
|
||||
writeHeader(writer, &headerOrigin)
|
||||
copyContent(reader, writer, *blockSize)
|
||||
|
||||
fmt.Println("\nCopy done")
|
||||
writer.Flush()
|
||||
}
|
||||
|
||||
} else {
|
||||
fmt.Println("No input file given")
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue