github.com/la5nta/wl2k-go@v0.11.8/catalog/position_report.go (about)

     1  // Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
     2  // Use of this source code is governed by the MIT-license that can be
     3  // found in the LICENSE file.
     4  
     5  // Package catalog provides helpers for using the Winlink 2000 catalog services.
     6  package catalog
     7  
     8  import (
     9  	"bytes"
    10  	"errors"
    11  	"fmt"
    12  	"math"
    13  	"time"
    14  
    15  	"github.com/la5nta/wl2k-go/fbb"
    16  )
    17  
    18  type PosReport struct {
    19  	Date     time.Time
    20  	Lat, Lon *float64 // In decimal degrees
    21  	Speed    *float64 // Unit not specified in winlink docs
    22  	Course   *Course
    23  	Comment  string // Up to 80 characters
    24  }
    25  
    26  type Course struct {
    27  	Digits   [3]byte
    28  	Magnetic bool
    29  }
    30  
    31  func NewCourse(degrees int, magnetic bool) (*Course, error) {
    32  	if degrees < 0 || degrees > 360 {
    33  		return nil, errors.New("degrees out of bounds [0,360]")
    34  	}
    35  	if degrees == 360 {
    36  		degrees = 0
    37  	}
    38  	c := Course{Magnetic: magnetic}
    39  	copy(c.Digits[:], []byte(fmt.Sprintf("%3d", degrees)))
    40  	return &c, nil
    41  }
    42  
    43  func (c Course) String() string {
    44  	if c.Magnetic {
    45  		return fmt.Sprintf("%sM", string(c.Digits[:]))
    46  	} else {
    47  		return fmt.Sprintf("%sT", string(c.Digits[:]))
    48  	}
    49  }
    50  
    51  func (p PosReport) Message(mycall string) *fbb.Message {
    52  	var buf bytes.Buffer
    53  	fmt.Fprintf(&buf, "DATE: %s\r\n", p.Date.UTC().Format(fbb.DateLayout))
    54  
    55  	if p.Lat != nil && p.Lon != nil {
    56  		fmt.Fprintf(&buf, "LATITUDE: %s\r\n", decToMinDec(*p.Lat, true))
    57  		fmt.Fprintf(&buf, "LONGITUDE: %s\r\n", decToMinDec(*p.Lon, false))
    58  	}
    59  	if p.Speed != nil {
    60  		fmt.Fprintf(&buf, "SPEED: %f\r\n", *p.Speed)
    61  	}
    62  	if p.Course != nil {
    63  		fmt.Fprintf(&buf, "COURSE: %s\r\n", *p.Course)
    64  	}
    65  	if len(p.Comment) > 0 {
    66  		fmt.Fprintf(&buf, "COMMENT: %s\r\n", p.Comment)
    67  	}
    68  
    69  	msg := fbb.NewMessage(fbb.PositionReport, mycall)
    70  
    71  	err := msg.SetBody(buf.String())
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  
    76  	msg.SetSubject("POSITION REPORT")
    77  	msg.AddTo("QTH")
    78  
    79  	return msg
    80  }
    81  
    82  // Format: 23-42.3N
    83  func decToMinDec(dec float64, latitude bool) string {
    84  	var sign byte
    85  	if latitude && dec > 0 {
    86  		sign = 'N'
    87  	} else if latitude && dec < 0 {
    88  		sign = 'S'
    89  	} else if !latitude && dec > 0 {
    90  		sign = 'E'
    91  	} else if !latitude && dec < 0 {
    92  		sign = 'W'
    93  	} else {
    94  		sign = ' '
    95  	}
    96  
    97  	deg := int(dec)
    98  	min := (dec - float64(deg)) * 60.0
    99  
   100  	var format string
   101  	if latitude {
   102  		format = "%02.0f-%07.4f%c"
   103  	} else {
   104  		format = "%03.0f-%07.4f%c"
   105  	}
   106  
   107  	return fmt.Sprintf(format, math.Abs(float64(deg)), math.Abs(min), sign)
   108  }