add numeric message encoding
This commit is contained in:
parent
83ba725969
commit
9bb56dc6c8
5 changed files with 155 additions and 21 deletions
10
README.md
10
README.md
|
@ -1,4 +1,4 @@
|
||||||
Go port of the POCSAG::Encode Perl module.
|
Golang port of the `POCSAG::Encode` Perl module extended to support Numeric as well as AlphaNumeric messages.
|
||||||
|
|
||||||
Example usage
|
Example usage
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ Example usage
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/kgolding/go-pocsagencode"
|
"github.com/kgolding/go-pocsagencode"
|
||||||
|
@ -14,12 +13,7 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
messages := []*pocsagencode.Message{
|
messages := []*pocsagencode.Message{
|
||||||
&pocsagencode.Message{1300100, "Hello Pager!"},
|
&pocsagencode.Message{1300100, "Hello Pager!", false},
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 50; i++ {
|
|
||||||
addr := uint32(1200000 + i*100)
|
|
||||||
messages = append(messages, &pocsagencode.Message{addr, fmt.Sprintf("Hello pager number %d", addr)})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Sending", len(messages), "messages")
|
log.Println("Sending", len(messages), "messages")
|
||||||
|
|
|
@ -1,29 +1,49 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
pc "github.com/kgolding/go-pocsagencode"
|
pc "github.com/kgolding/go-pocsagencode"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 3 {
|
var num bool
|
||||||
fmt.Printf("Usage: %s <pager addr> <text message>\ne.g. %s 13100100 'Hello world!'\n", os.Args[0], os.Args[0])
|
var debug bool
|
||||||
|
|
||||||
|
flag.BoolVar(&num, "num", false, "send as numeric message")
|
||||||
|
flag.BoolVar(&debug, "v", false, "verbose logging")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(flag.Args()) != 2 {
|
||||||
|
me := filepath.Base(os.Args[0])
|
||||||
|
fmt.Printf(`
|
||||||
|
Usage: %s [-num] <pager addr> <text or numeric message>
|
||||||
|
Examples: %s 13100100 'Hello world!'
|
||||||
|
%s -num 13100100 9876\n`, me, me, me)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
addr, err := strconv.Atoi(os.Args[1])
|
addr, err := strconv.Atoi(flag.Arg(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Invalid pager addr - must be a number")
|
fmt.Println("Invalid pager addr - must be a number")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
messages := []*pc.Message{
|
messages := []*pc.Message{
|
||||||
&pc.Message{
|
&pc.Message{
|
||||||
Addr: uint32(addr),
|
Addr: uint32(addr),
|
||||||
Content: os.Args[2],
|
Content: flag.Arg(0),
|
||||||
|
IsNumeric: num,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
pc.SetLogger(log.New(os.Stdout, "", log.Lshortfile))
|
||||||
|
}
|
||||||
|
|
||||||
burst, _ := pc.Generate(messages)
|
burst, _ := pc.Generate(messages)
|
||||||
|
|
||||||
fmt.Printf("Message: %s\n\n", burst.String())
|
fmt.Printf("Message: %s\n\n", burst.String())
|
||||||
|
|
|
@ -9,12 +9,12 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
messages := []*pocsagencode.Message{
|
messages := []*pocsagencode.Message{
|
||||||
&pocsagencode.Message{1300100, "Hello Pager!"},
|
&pocsagencode.Message{1300100, "Hello Pager!", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 50; i++ {
|
for i := 0; i < 50; i++ {
|
||||||
addr := uint32(1200000 + i*100)
|
addr := uint32(1200000 + i*100)
|
||||||
messages = append(messages, &pocsagencode.Message{addr, fmt.Sprintf("Hello pager number %d", addr)})
|
messages = append(messages, &pocsagencode.Message{addr, fmt.Sprintf("Hello pager number %d", addr), false})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Sending", len(messages), "messages")
|
log.Println("Sending", len(messages), "messages")
|
||||||
|
|
|
@ -6,8 +6,9 @@ import (
|
||||||
|
|
||||||
// Message is a single POCSAG Alphanumeric message
|
// Message is a single POCSAG Alphanumeric message
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Addr uint32
|
Addr uint32
|
||||||
Content string
|
Content string
|
||||||
|
IsNumeric bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var logger *log.Logger
|
var logger *log.Logger
|
||||||
|
@ -185,6 +186,81 @@ func appendContentText(content string) (int, Burst) {
|
||||||
return pos, out
|
return pos, out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendContentNumeric appends numeric message content to the transmission blob
|
||||||
|
func appendContentNumeric(content string) (int, Burst) {
|
||||||
|
out := make(Burst, 0)
|
||||||
|
debugf("appendContentNumeric: %s", content)
|
||||||
|
|
||||||
|
// 084 2.6]195-3U7[
|
||||||
|
charMap := map[byte]byte{
|
||||||
|
'0': 0, '8': 1, '4': 2, ' ': 3, '2': 4, '.': 5, '6': 6, ']': 7,
|
||||||
|
'1': 8, '9': 9, '5': 10, '-': 11, '3': 12, 'U': 13, '7': 14, '[': 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitpos := 0
|
||||||
|
word := uint32(0)
|
||||||
|
leftbits := 0
|
||||||
|
pos := 0
|
||||||
|
|
||||||
|
// walk through characters in message
|
||||||
|
for i, r := range content {
|
||||||
|
var char byte
|
||||||
|
var ok bool
|
||||||
|
// set char from the charMap, and skip the character is not in the map
|
||||||
|
if char, ok = charMap[byte(r)]; !ok {
|
||||||
|
debugf("skipping invlaid char '%s'", string(r))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
debugf(" char %d: %d [%X]\n", i, char, char)
|
||||||
|
|
||||||
|
// if the bits won't fit:
|
||||||
|
if bitpos+4 > 20 {
|
||||||
|
space := 20 - bitpos
|
||||||
|
// leftbits least significant bits of $char are left over in the next word
|
||||||
|
leftbits = 4 - space
|
||||||
|
debugf(" bits of char won't fit since bitpos is %d, got %d bits free, leaving %d bits in next word", bitpos, space, leftbits)
|
||||||
|
}
|
||||||
|
|
||||||
|
word |= (uint32(char) << uint(31-4-bitpos))
|
||||||
|
|
||||||
|
bitpos += 4
|
||||||
|
|
||||||
|
if bitpos >= 20 {
|
||||||
|
debugf(" appending word: %X\n", word)
|
||||||
|
out = append(out, appendMessageCodeword(word))
|
||||||
|
pos++
|
||||||
|
word = 0
|
||||||
|
bitpos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftbits > 0 {
|
||||||
|
word |= (uint32(char) << uint(31-leftbits))
|
||||||
|
bitpos = leftbits
|
||||||
|
leftbits = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bitpos > 0 {
|
||||||
|
debugf(" got %d bits in word at end of text, word: %X", bitpos, word)
|
||||||
|
step := 0
|
||||||
|
for bitpos < 20 {
|
||||||
|
if step == 2 {
|
||||||
|
word |= (1 << uint(30-bitpos))
|
||||||
|
}
|
||||||
|
bitpos++
|
||||||
|
step++
|
||||||
|
if step == 4 {
|
||||||
|
step = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = append(out, appendMessageCodeword(word))
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos, out
|
||||||
|
}
|
||||||
|
|
||||||
// appendMessage appends a single message to the end of the transmission blob.
|
// appendMessage appends a single message to the end of the transmission blob.
|
||||||
func appendMessage(startpos int, msg *Message) (int, Burst) {
|
func appendMessage(startpos int, msg *Message) (int, Burst) {
|
||||||
// expand the parameters of the message
|
// expand the parameters of the message
|
||||||
|
@ -217,7 +293,13 @@ func appendMessage(startpos int, msg *Message) (int, Burst) {
|
||||||
pos++
|
pos++
|
||||||
|
|
||||||
// Next, append the message contents
|
// Next, append the message contents
|
||||||
contentEncLen, contentEnc := appendContentText(content)
|
var contentEncLen int
|
||||||
|
var contentEnc Burst
|
||||||
|
if msg.IsNumeric {
|
||||||
|
contentEncLen, contentEnc = appendContentNumeric(content)
|
||||||
|
} else {
|
||||||
|
contentEncLen, contentEnc = appendContentText(content)
|
||||||
|
}
|
||||||
|
|
||||||
tx = append(tx, contentEnc...)
|
tx = append(tx, contentEnc...)
|
||||||
pos += contentEncLen
|
pos += contentEncLen
|
||||||
|
@ -296,7 +378,7 @@ func selectMsg(pos int, msgListRef []*Message) int {
|
||||||
// in the next Generate() call and sent in the next brrraaaap.
|
// in the next Generate() call and sent in the next brrraaaap.
|
||||||
func Generate(messages []*Message, optionFns ...OptionFn) (Burst, []*Message) {
|
func Generate(messages []*Message, optionFns ...OptionFn) (Burst, []*Message) {
|
||||||
options := &Options{
|
options := &Options{
|
||||||
MaxLen: 3000,
|
MaxLen: 2000,
|
||||||
PreambleBits: 576,
|
PreambleBits: 576,
|
||||||
}
|
}
|
||||||
for _, opt := range optionFns {
|
for _, opt := range optionFns {
|
||||||
|
|
|
@ -1,13 +1,51 @@
|
||||||
package pocsagencode
|
package pocsagencode
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Encode(t *testing.T) {
|
func init() {
|
||||||
|
SetLogger(log.New(os.Stdout, "POCSAG ", log.LstdFlags))
|
||||||
|
// Comment below to enable debug logging
|
||||||
|
SetLogger(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Encode_Numeric(t *testing.T) {
|
||||||
|
|
||||||
|
enc, left := Generate([]*Message{
|
||||||
|
&Message{1300100, "12[3]", true},
|
||||||
|
})
|
||||||
|
if len(left) != 0 {
|
||||||
|
t.Errorf("expect no message left, got %v", left)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect := Burst{
|
||||||
|
// 18 words, 576 bits of preamble
|
||||||
|
0xAAAAAAAA, 0xAAAAAAAA,
|
||||||
|
0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA,
|
||||||
|
0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA, 0xAAAAAAAA,
|
||||||
|
// The real data starts here
|
||||||
|
0x7CD215D8, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197, 0x7A89C197,
|
||||||
|
0x7A89C197, 0x4F5A0109, 0xC27E3D14, 0x7A89C197, 0x7A89C197,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(enc) != len(expect) {
|
||||||
|
t.Errorf("expected:\n%s\ngot:\n%s\n", expect, enc)
|
||||||
|
} else {
|
||||||
|
for i, w := range expect {
|
||||||
|
if w != enc[i] {
|
||||||
|
t.Errorf("expected:%X got:%X at index %d\n", w, enc[i], i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Encode_Alpha(t *testing.T) {
|
||||||
// SetLogger(log.New(os.Stdout, "POCSAG ", log.LstdFlags))
|
// SetLogger(log.New(os.Stdout, "POCSAG ", log.LstdFlags))
|
||||||
enc, left := Generate([]*Message{
|
enc, left := Generate([]*Message{
|
||||||
&Message{1300100, "happy christmas!"},
|
&Message{1300100, "happy christmas!", false},
|
||||||
})
|
})
|
||||||
if len(left) != 0 {
|
if len(left) != 0 {
|
||||||
t.Errorf("expect no message left, got %v", left)
|
t.Errorf("expect no message left, got %v", left)
|
||||||
|
|
Loading…
Add table
Reference in a new issue