123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961 |
- package monitor
-
- import (
- "bytes"
- "code.beefchicken.com/trimtools/dotx"
- "encoding/binary"
- "fmt"
- // "github.com/jacobsa/go-serial/serial"
- "io"
- "strconv"
- "strings"
- "time"
- )
-
- const (
- readWriteBlockSize = 244
-
- NrmCmdGetSerial = 0x06
- NrmCmdRSerial = 0x07
- NrmCmdGetOpt = 0x4A
- NrmCmdRetOpt = 0x4B
- NrmCmdModeChange = 0x87
-
- MonCmdWriteMem = 0x80
- MonCmdReadMem = 0x82
- MonCmdBaudRate = 0x86
- MonCmdReset = 0x88
- MonCmdJmp = 0x89
-
- MonCmdReadMemAck = 0x92
- )
-
- type Monitor struct {
- receiverType ReceiverType
- Dummy bool
- inMonitor bool
- receiver *Receiver
- seekPos uint32
- }
-
- type ReceiverState int
-
- const (
- ReceiverUnexpected ReceiverState = iota
- ReceiverUnresponsive
- ReceiverNormal
- ReceiverInMonitor
- )
-
- type ReceiverType int
-
- const (
- ReceiverType4000 ReceiverType = iota
- ReceiverType4400
- )
-
- func NewMonitor(receiver *Receiver) *Monitor {
- return &Monitor{receiver: receiver}
- }
-
- func (m *Monitor) StartMonitor() error {
-
- if m.Dummy {
- m.inMonitor = true
- return nil
- }
- fmt.Println("Starting monitor mode...")
-
- // once we have something that looks like a receiver on the line, figure
- // out what mode it's in.
- state, err := m.receiver.ProbeReceiverState()
- if err != nil {
- return err
- }
-
- if state == ReceiverUnresponsive {
- return fmt.Errorf("receiver unresponsive")
- }
-
- if state == ReceiverNormal {
- // get config
- conf, err := m.CmdGetRuntimeConfig()
- if err != nil {
- return err
- }
- fmt.Printf("PPU Version = %.2f\n", conf.BootRomVersion)
-
- opts, err := m.CmdGetOptions()
- if opts.ReceiverType == 3 { // gauss
-
- }
- // switch to monitor mode
- err = m.receiver.DoAckCommand(NrmCmdModeChange, []byte{0x57}) // 0x57 is what an ack at 38400 looks like at 9600 baud.
- if err != nil {
- return err
- }
-
- // When the monitor starts, it reverts back to 9600-N-8-1, so we have to
- // re-configure the serial port.
- err = m.receiver.useSlowBaudRate()
- if err != nil {
- return err
- }
-
- // It can take a while to boot into monitor mode
- err = m.receiver.waitForMonitor()
- if err != nil {
- return err
- }
- }
- // reconfigure monitor to speak at 38400-N-8-1
- // 0x6D = 4800
- // 0x37 = 9600
- // 0x1B = 19200
- // 0x0E = 38400
- // This appears to respond with 0xFE, but that is just is what a 38400
- // baud ACK (0x06) looks like when received at 9600 baud, and depending
- // on where in the bit the UART samples, different UARTs might see
- // different values, so we just ignore any no-ack error.
-
- err = m.receiver.DoAckCommand(MonCmdBaudRate, []byte{0x00, 0x0E})
- if err != nil {
- if _, ok := err.(*ProtocolError); !ok {
- return err
- }
- }
-
- // re-configure port for 38400 baud
- err = m.receiver.useFastBaudRate()
- if err != nil {
- return err
- }
-
- // Make sure baud switch was successful
- err = m.receiver.waitForMonitor()
- if err != nil {
- return err
- }
-
- // }
- m.inMonitor = true
-
- // we should be in monitor mode now.
-
- fmt.Printf("Setting chip selects...\n")
-
- err = m.setChipSelects()
- if err != nil {
- return err
- }
-
- m.inMonitor = true
-
- fmt.Println("Monitor mode active.")
-
- return nil
- }
-
- func (m *Monitor) setChipSelects() error {
-
- // I don't understand the mechanism the PPU uses to communicate to the
- // system memory. The chip select registers only apply to signals originating
- // from the internal bus, so if the PPU is just taking over the bus, then the
- // chip selects wouldn't matter. But they definitely do, and if they're not
- // set properly, the PPU ends up seeing the wrong memory.
-
- // Maybe the PPU just loads a small ROM monitor into RAM, then hands control
- // back to the CPU? Hmmm.
-
- err := m.WriteMemory(0x00FFFA58, []uint16{
- 0x1004, // CSBAR3 128k @ 0x100000
- 0x7B71, // CSOR3 Change DSACK to 13 wait
- })
-
- if err != nil {
- return err
- }
-
- err = m.WriteMemory(0x00100000, []byte{0x55, 0x55, 0xAA, 0xAA})
- if err != nil {
- return err
- }
-
- b, err := m.ReadMemory(0x00100000, 4)
- if err != nil {
- return err
- }
-
- if bytes.Compare(b, []byte{0x55, 0x55, 0xAA, 0xAA}) == 0 {
- fmt.Printf("SE/SSE/SSi H/W target receiver detected.\n")
- fmt.Printf("Setting SE/SSE/SSi chip selects...\n")
-
- err = m.WriteMemory(0x00FFFA54, []uint16{
- 0x7007, // CSBAR2
- 0x7871, // CSOR2
- })
- if err != nil {
- return err
- }
-
- err = m.WriteMemory(0x00FFFA46, []uint16{
- 0x03F5, // CSPAR1
- })
- if err != nil {
- return err
- }
-
- err = m.WriteMemory(0x00FFFA58, []uint16{
- 0x1004, // CSBAR3
- 0x7BF1, // CSOR3
- })
- if err != nil {
- return err
- }
-
- m.receiverType = ReceiverType4000
-
- } else {
- fmt.Printf("Cheetah/7400 MSi target receiver detected.\n")
- fmt.Printf("Setting Cheetah/7400 MSi chip selects...\n")
-
- err = m.WriteMemory(0x00FFFA48, []uint16{
- 0x0005, // CSBARBT
- 0x7831, // CSORBT
- 0x0405, // CSBAR0
- 0x7831, // CSOR0
- 0x7005, // CSBAR1
- 0x7831, // CSOR1
- 0x7405, // CSBAR2
- 0x7831, // CSOR2
- })
- if err != nil {
- return err
- }
- m.receiverType = ReceiverType4400
- }
- return nil
- }
-
- // Stop sends a reboot, which disconnects the monitor, and reconfigures the port back
- // to the user configuration.
- func (m *Monitor) Stop() error {
- return m.CmdResetReceiver()
- }
-
- // Close closes the serial port associated with the monitor
- func (m *Monitor) Close() {
- m.receiver.port.Close()
- }
-
- // ReadMemory reads the device memory, breaking the read up into multiple
- // commands as needed.
- func (m *Monitor) ReadMemory(addr uint32, length int) ([]byte, error) {
- if !m.inMonitor {
- return nil, fmt.Errorf("receiver not in monitor")
- }
- //fmt.Printf("ReadMemory: addr=%08X length=%d\n", addr, length)
-
- out := make([]byte, 0)
- for i := 0; i < length; i += readWriteBlockSize {
- l := readWriteBlockSize
- if i+l > length {
- l = length - i
- }
-
- blockAddr := addr + uint32(i)
-
- b, err := m.CmdReadMemory(blockAddr, uint8(l))
- if err != nil {
- return nil, err
- }
- out = append(out, b...)
- }
- return out, nil
- }
-
- func (m *Monitor) CmdReadMemory(addr uint32, length uint8) ([]byte, error) {
- b := make([]byte, 5)
- binary.BigEndian.PutUint32(b[0:4], addr)
- b[4] = byte(length)
-
- //fmt.Printf("CmdReadMemory: addr=%08X length=%d\n", blockAddr, l)
-
- var err error
- const maxRetries = 3
-
- for r := 0; r < maxRetries; r++ {
- if r > 0 {
- fmt.Printf("CmdReadMemory: read failed: %v. Retrying.\n", err)
- }
- var reply *CommandReply
- if reply, err = m.receiver.DoStxEtxCommand(MonCmdReadMem, b); err != nil {
- continue
- }
- if reply.Command != MonCmdReadMemAck || reply.Status != 0 {
- err = newProtocolError("unexpected response from read memory")
- continue
- }
- if replyAddr := binary.BigEndian.Uint32(reply.Data[0:4]); replyAddr != addr {
- err = newProtocolError("address mismatch in read reply. expected 0x%06X, received 0x%06X", addr, replyAddr)
- continue
- }
- return reply.Data[4:], nil
- }
- return nil, err
- }
-
- // WriteMemory writes the device memory, breaking the write up into multiple
- // commands as needed.
- func (m *Monitor) WriteMemory(addr uint32, data interface{}) error {
- if !m.inMonitor {
- return fmt.Errorf("receiver not in monitor")
- }
-
- buf := &bytes.Buffer{}
- binary.Write(buf, binary.BigEndian, data)
- b := buf.Bytes()
- length := len(b)
-
- for i := 0; i < length; i += readWriteBlockSize {
- l := readWriteBlockSize
- if i+l > length {
- l = length - i
- }
-
- blockAddr := addr + uint32(i)
-
- err := m.CmdWriteMemory(blockAddr, b[i:i+l])
- if err != nil {
- return err
- }
- }
- return nil
- }
-
- func (m *Monitor) CmdWriteMemory(addr uint32, data []byte) error {
- const maxRetries = 3
- buf := make([]byte, 4)
- // for writes the address words are in little endian order, but the bytes
- // within the words are in big endian order
- binary.BigEndian.PutUint16(buf[0:2], uint16(addr&0xFFFF))
- binary.BigEndian.PutUint16(buf[2:4], uint16(addr>>16&0xFFFF))
- buf = append(buf, data...)
-
- var err error
- for r := 0; r < maxRetries; r++ {
- if r > 0 {
- fmt.Printf("CmdWriteMemory: write failed: %v. Retrying.\n", err)
- }
- err = m.receiver.DoAckCommand(MonCmdWriteMem, buf)
- if err == nil {
- break
- }
-
- }
-
- if err != nil {
- return err
- }
- return nil
- }
-
- // Jump calls the monitor's jump command.
- func (m *Monitor) CmdJump(addr uint32) error {
- if !m.inMonitor {
- return fmt.Errorf("receiver not in monitor")
- }
- b := make([]byte, 4)
- binary.BigEndian.PutUint32(b[0:4], addr)
-
- return m.receiver.DoAckCommand(MonCmdJmp, b)
- }
-
- // ResetReceiver calls the monitor's reset command.
- func (m *Monitor) CmdResetReceiver() error {
- if !m.inMonitor {
- return fmt.Errorf("receiver not in monitor")
- }
- err := m.receiver.SendStxEtxRequest(MonCmdReset, []byte{})
- if err != nil {
- return err
- }
- m.inMonitor = false
-
- // it seems that if I switch the baud rate too soon, the UART will still
- // be transmitting the above data, which will corrupt the command.
- time.Sleep(2 * time.Second)
-
- // revert the serial port
- err = m.receiver.useUserBaudRate()
- if err != nil {
- return err
- }
-
- return nil
- }
-
- // ReceiverInfo holds all the information we can glean from the receiver by
- // probing its memory.
- type ReceiverInfo struct {
- Name1 string
- Name2 string
- SerialNumber int
- SerialNumberString string
- OptionMemory []byte
- ConfigMemory []byte
- FirmwareDate time.Time
- FirmwareVersionMajor int
- FirmwareVersionMinor int
- FirmwareVersion float64
- ReceiverType ReceiverType
- Code1Start uint32
- Code1End uint32
- Code1Checksum uint32
- Code2Start uint32
- Code2End uint32
- Code2Checksum uint32
- }
-
- // PrintOptions emulates the output of the loader.exe 'O' monitor mode command
- func (info *ReceiverInfo) PrintOptions() {
-
- // Print receiver identity information
- fmt.Printf(" name1:%s\n", info.Name1)
- fmt.Printf(" name2:%s\n", info.Name2)
- fmt.Printf("ASCII serial #:%14s\n", info.SerialNumberString)
- fmt.Printf(" int serial #:%14d\n", info.SerialNumber)
-
- // Print Header
- for i := 0; i <= 48; i += 12 {
- fmt.Printf("Options %02d-%02d ", i, i+11)
- }
- fmt.Printf("\n")
-
- // Print option values
- optNames := []string{
- "opt #00", "opt #01", "Locator", "opt #03", "RTCMAsci", "CyclPrnt", "PosStats", "TailBuoy", "PFinder", "LandSeis", "RTCMnetw", "CarPhDis",
- "Caltrans", "MxL1only", "RmtDnlod", "Demo", "opt #16", "RT17dsab", "LclDatum", "opt #19", "DualFreq", "SerPorts", "MulDatum", "ExtFreq",
- "EventMrk", "1PPS", "opt #26", "opt #27", "RTCM in", "RTCM out", "SyncLimt", "NMEA out", "NGS", "opt #33", "MultWYPT", "LctrCrPh",
- "Kinematc", "Config'd", "MemLimit", "RTK-L1", "RTK-OTF", "IONOFREE", "opt #42", "opt #43", "opt #44", "opt #45", "opt #46", "opt #47",
- "opt #48", "opt #49", "opt #50", "opt #51", "opt #52", "opt #53", "opt #54", "opt #55", "opt #56", "opt #57", "opt #58", "opt #59",
- }
- for i := 0; i < 12; i++ {
- for j := i; j <= i+48; j += 12 {
- fmt.Printf("$%02x-%-11s", info.OptionMemory[j], optNames[j])
- }
- fmt.Printf("\n")
- }
-
- }
-
- func (info *ReceiverInfo) PrintVersions() {
- // TODO: empty string in param 3 should say '(TEST)' when in test mode
- fmt.Printf(" Code Version: %1d.%02d %s Date: %s\n", info.FirmwareVersionMajor, info.FirmwareVersionMinor, "", info.FirmwareDate.Format("02-Jan-06"))
- fmt.Printf(" Code1: %06X-%06X Code2: %06X-%06X\n", info.Code1Start, info.Code1End-1, info.Code2Start, info.Code2End-1)
- fmt.Printf("Checksums: %06X %06X\n", info.Code1Checksum, info.Code2Checksum)
- }
-
- // GetReceiverInfo reads receiver information out of the receiver. Unlike GetOptions
- // and GetRuntimeConfig, this reads the values straight out of memory.
- func (m *Monitor) GetReceiverInfo() (*ReceiverInfo, error) {
- if !m.inMonitor {
- return nil, fmt.Errorf("receiver not in monitor")
- }
- info := &ReceiverInfo{}
-
- // Read the receiver-specific configuration.
- // 0x80600 and 0x22d both contain the same data, but for some reason some
- // code reads from one area and some from the other.
- b, err := m.ReadMemory(0x80600, 0x97) // or 0x80600? I'm so confused.
- if err != nil {
- return nil, err
- }
- info.Name1 = cStrtoGoStr(b[60:96])
- info.Name2 = cStrtoGoStr(b[96:132])
- info.SerialNumberString = cStrtoGoStr(b[132:147])
- info.SerialNumber = int(binary.BigEndian.Uint32(b[147:151]))
- info.OptionMemory = b[0:60]
- info.ReceiverType = m.receiverType
-
- configBase := uint32(0x000001F0)
-
- // read the configuration memory
- b, err = m.ReadMemory(configBase, 0x3A)
- if err != nil {
- return nil, err
- }
- info.ConfigMemory = b
-
- date := int(binary.BigEndian.Uint32(info.ConfigMemory[0x222-configBase:]))
- month := date / 10000
- day := date / 100 % 100
- year := date % 100
- if year >= 80 {
- year += 1900
- } else {
- year += 2000
- }
- info.FirmwareDate = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
-
- ver := int(binary.BigEndian.Uint16(info.ConfigMemory[0x204-configBase:]))
- info.FirmwareVersionMajor = ver / 100
- info.FirmwareVersionMinor = ver % 100
- info.FirmwareVersion = float64(ver) / 100
-
- info.Code1Start = binary.BigEndian.Uint32(info.ConfigMemory[0x1f0-configBase:])
- info.Code1End = binary.BigEndian.Uint32(info.ConfigMemory[0x1f4-configBase:])
- info.Code2Start = binary.BigEndian.Uint32(info.ConfigMemory[0x1f8-configBase:])
- info.Code2End = binary.BigEndian.Uint32(info.ConfigMemory[0x1fc-configBase:])
-
- // read checksums
- b, err = m.ReadMemory(info.Code1End-12, 4)
- if err != nil {
- return nil, err
- }
- info.Code1Checksum = binary.BigEndian.Uint32(b)
- b, err = m.ReadMemory(info.Code2End-12, 4)
- if err != nil {
- return nil, err
- }
- info.Code2Checksum = binary.BigEndian.Uint32(b)
-
- if info.FirmwareVersion > 5.60 {
- }
- return info, nil
- }
-
- func cStrtoGoStr(b []byte) string {
- i := bytes.IndexByte(b, 0)
- if i < 0 {
- return string(b)
- }
- return string(b[:i])
- }
-
- type RuntimeConfig struct {
- SerialNumber string
- ReceiverType string
- NavProcessVersion float64
- SigProcessVersion float64
- BootRomVersion float64
- AntennaSerial string
- AntennaType string
- Channels string
- ChannelsL1 string
- }
-
- func progressBar(width, progress, total int) string {
- progressBlocks := []rune{'▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'}
- p := float64(progress) / float64(total)
- blocks := int(float64(width) * p)
- eighths := int(float64(width)*p*8) % 8
- bar := strings.Repeat("█", blocks)
- blankWidth := width - blocks - 1
- if blankWidth < 0 {
- blankWidth = 0
- }
- blank := strings.Repeat("-", blankWidth)
- return fmt.Sprintf("(%0.2f%%) %s%c%s\n", p*100, bar, progressBlocks[eighths], blank)
- }
-
- type codeBlock struct {
- start uint32
- end uint32
- }
-
- func (m *Monitor) CaptureReceiverFirmware() (*dotx.DotXFile, error) {
-
- info, err := m.GetReceiverInfo()
- if err != nil {
- return nil, err
- }
- dotX := dotx.NewDotX()
-
- // these chip selects are hard-coded into the first two records in the file
- dotX.AddBlock(0x00FFFA54, []byte{0x70, 0x07, 0x78, 0x71})
- dotX.AddBlock(0x00FFFA46, []byte{0x03, 0xF5})
-
- // then the code blocks
- codeBlocks := []*dotx.Block{
- {StartAddr: info.Code1Start, EndAddr: info.Code1End},
- {StartAddr: info.Code2Start, EndAddr: info.Code2End},
- }
-
- totalBytes := 0
-
- for _, cb := range codeBlocks {
- totalBytes += cb.Len()
- }
-
- bytesWriten := 0
-
- for _, block := range codeBlocks {
- blockLen := block.Len()
- b, err := m.ReadMemory(block.StartAddr, blockLen)
- if err != nil {
- return nil, err
- }
- block.Bytes = b
- dotX.Blocks = append(dotX.Blocks, block)
- bytesWriten += blockLen
- }
-
- // func(i uint32) {
- // fmt.Printf("$%08X %s\r", i, progressBar(50, int(i-block.StartAddr)+bytesWriten, totalBytes))
- // }
-
- return dotX, nil
- }
-
- func (m *Monitor) CmdGetRuntimeConfig() (*RuntimeConfig, error) {
- if m.inMonitor {
- return nil, fmt.Errorf("receiver not in normal mode")
- }
- r, err := m.receiver.DoStxEtxCommand(NrmCmdGetSerial, []byte{})
- if err != nil {
- return nil, err
- }
-
- if r.Command != NrmCmdRSerial || r.Status != 0 {
- return nil, newProtocolError("unexpected response from read memory")
- }
-
- conf := &RuntimeConfig{}
-
- conf.SerialNumber = strings.TrimSpace(cStrtoGoStr(r.Data[0:8]))
- conf.ReceiverType = strings.TrimSpace(cStrtoGoStr(r.Data[8:16]))
-
- conf.NavProcessVersion, _ = strconv.ParseFloat(cStrtoGoStr(r.Data[16:21]), 64)
- conf.NavProcessVersion /= 100
- conf.SigProcessVersion, _ = strconv.ParseFloat(cStrtoGoStr(r.Data[21:26]), 64)
- conf.SigProcessVersion /= 100
- conf.BootRomVersion, _ = strconv.ParseFloat(cStrtoGoStr(r.Data[26:31]), 64)
- conf.BootRomVersion /= 100
-
- conf.AntennaSerial = strings.TrimSpace(cStrtoGoStr(r.Data[31:39]))
- conf.AntennaType = strings.TrimSpace(cStrtoGoStr(r.Data[39:41]))
- conf.Channels = strings.TrimSpace(cStrtoGoStr(r.Data[41:43]))
- conf.ChannelsL1 = strings.TrimSpace(cStrtoGoStr(r.Data[43:45]))
-
- return conf, nil
- }
-
- type Options struct {
- ElevationMask int
- PDOPMask float64
- SyncTime float64
- FastestMeasRate float64
- CurrentPortID int
- PortsAvailable int
- L1L2Operation int
- CarrierPhase bool
- KinematicMode bool
- LocatorMode bool
- PowerUpOption bool
- RTKOption bool
- NMEA0183Outputs bool
- RTCM1Input bool
- RTCM2Input bool
- RTCM1Output bool
- RTCM2Output bool
- NavOption bool
- FirmwareUpdate bool
- EventMarker int
- PulsePerSec int
- ExtTimeInput int
- CoCOM bool
- MemoryInstalled int
- MemoryUsed int
- RTCMNetwork bool
- DataFormat int
- DataOffset int
- PositionStatistics bool
- RemoteDownload bool
- LocalDatums bool
- RealTimeSurveyData bool
- CalTrans bool
- ReceiverType int
- }
-
- func (m *Monitor) CmdGetOptions() (*Options, error) {
- if m.inMonitor {
- return nil, fmt.Errorf("receiver not in normal mode")
- }
- r, err := m.receiver.DoStxEtxCommand(NrmCmdGetOpt, []byte{})
- if err != nil {
- return nil, err
- }
-
- if r.Command != NrmCmdRetOpt || r.Status != 0 {
- return nil, newProtocolError("unexpected response from read memory")
- }
-
- opts := &Options{
- ElevationMask: int(r.Data[0]),
- PDOPMask: float64(r.Data[1]) * 0.1,
- SyncTime: float64(binary.BigEndian.Uint16(r.Data[2:4])) * 1,
- FastestMeasRate: float64(binary.BigEndian.Uint16(r.Data[4:6])) * .1,
- CurrentPortID: int(r.Data[6]),
- PortsAvailable: int(r.Data[7]),
- L1L2Operation: int(r.Data[8]),
- CarrierPhase: r.Data[9] == 1,
- KinematicMode: r.Data[10] == 1,
- LocatorMode: r.Data[11] == 1,
- PowerUpOption: r.Data[12] == 1,
- RTKOption: r.Data[13] == 1,
- NMEA0183Outputs: r.Data[18] == 1,
- RTCM1Input: r.Data[19] == 1,
- RTCM2Input: r.Data[20] == 1,
- RTCM1Output: r.Data[21] == 1,
- RTCM2Output: r.Data[22] == 1,
- NavOption: r.Data[23] == 1,
- FirmwareUpdate: r.Data[24] == 1,
- EventMarker: int(r.Data[25]),
- PulsePerSec: int(r.Data[26]),
- ExtTimeInput: int(r.Data[27]),
- CoCOM: r.Data[28] == 1,
- MemoryInstalled: int(binary.BigEndian.Uint16(r.Data[29:31])),
- MemoryUsed: int(r.Data[31]),
- RTCMNetwork: r.Data[32] == 1,
- DataFormat: int(r.Data[34]),
- DataOffset: int(binary.BigEndian.Uint16(r.Data[35:37])),
- PositionStatistics: r.Data[37] == 1,
- RemoteDownload: r.Data[38] == 1,
- LocalDatums: r.Data[39] == 1,
- RealTimeSurveyData: r.Data[40] == 1,
- CalTrans: r.Data[41] == 1,
- ReceiverType: int(r.Data[44]),
- }
-
- return opts, nil
- }
-
- func (m *Monitor) ProgramReceiver(fw *dotx.DotXFile) error {
- fmt.Printf("Writing receiver program...\n")
- save := make([]byte, 8)
- totalBytes := fw.TotalBytes()
- t := 0
- var err error
- progressBarWidth := 50
- progressBlocks := []rune{'▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'}
-
- // set fixFailedWrite to true to have the code just correct the first 8
- // bytes of memory to remove the boot blocker. This is a kludge for testing.
- fixFailedWrite := false
-
- for i, block := range fw.Blocks {
- if fixFailedWrite && i > 2 {
- // all we want are the chip selects and CODE1
- continue
- }
- recs := block.ToRecords(244)
- fmt.Printf("Writing block %d ($%08X - $%08X)...\n", i, block.StartAddr, block.EndAddr)
- for _, rec := range recs {
- t += len(rec.Bytes)
- p := float64(t) / float64(totalBytes)
- blocks := int(float64(progressBarWidth) * p)
- eighths := int(float64(progressBarWidth)*p*8) % 8
- bar := strings.Repeat("█", blocks)
- blankWidth := progressBarWidth - blocks - 1
- if blankWidth < 0 {
- blankWidth = 0
- }
- blank := strings.Repeat("-", blankWidth)
-
- progressBar := fmt.Sprintf("%s%c%s", bar, progressBlocks[eighths], blank)
- fmt.Printf("$%08X (%0.2f%%) %s\r", rec.Addr, p*100, progressBar)
-
- if rec.Addr == 0x00000000 {
- // This replaces the first 8 bytes of the firmware image with
- // 4E72270000000000. The first 8 bytes are the stack pointer and
- // reset vector. The first value is actually a STOP #$2700
- // instruction, and the 0x00000000 reset vector means that STOP
- // is the only thing that will be executed. This saves the intended
- // value to write to memory at the end of the firmware upgrade
- // process, guaranteeing that the receiver will not function
- // with a half-baked firmware. Crafty, Trimble.
- b2 := make([]byte, len(rec.Bytes))
- copy(b2, rec.Bytes)
- copy(save[0:8], rec.Bytes[0:8])
- binary.BigEndian.PutUint32(b2[0:], 0x4E722700)
- binary.BigEndian.PutUint32(b2[4:], 0x00000000)
- err = m.WriteMemory(rec.Addr, b2)
-
- if fixFailedWrite {
- // for a fixFailedWrite all I care about is the first record.
- break
- }
- } else {
- err = m.WriteMemory(rec.Addr, rec.Bytes)
- }
- if err != nil {
- return err
- }
- if m.Dummy {
- time.Sleep(1 * time.Millisecond)
- }
- }
- fmt.Printf("% 80s\r", "")
- }
- fmt.Printf("\rDone. %d bytes written.\n", t)
- err = m.WriteMemory(0x00000000, save)
- if err != nil {
- return err
- }
-
- fmt.Printf("Rebooting receiver.\n")
- return m.CmdResetReceiver()
- }
-
- // Read implements the io.ReadSeeker interface
- func (m *Monitor) Read(p []byte) (n int, err error) {
- p, err = m.ReadMemory(m.seekPos, len(p))
- m.seekPos += uint32(len(p))
- return len(p), err
- }
-
- // Seek implements the io.ReadSeeker interface
- func (m *Monitor) Seek(offset int64, whence int) (int64, error) {
- var abs int64
- switch whence {
- case io.SeekStart:
- abs = offset
- case io.SeekCurrent:
- abs = int64(m.seekPos) + offset
- default:
- return 0, fmt.Errorf("Seek: invalid whence")
- }
- if abs < 0 {
- return 0, fmt.Errorf("Seek: negative position")
- }
- m.seekPos = uint32(abs)
- return abs, nil
- }
-
- func ReadMemory(f io.ReadSeeker, addr uint32, length int) ([]byte, error) {
- _, err := f.Seek(int64(addr), io.SeekStart)
- if err != nil {
- return nil, err
- }
- b := make([]byte, length)
- _, err = f.Read(b)
- if err != nil {
- return nil, err
- }
- return b, nil
- }
-
- type RawReceiverInfo struct {
- Options [60]byte
- Name1 [36]byte
- Name2 [36]byte
- SerialNumberString [15]byte
- SerialNumber uint32
- }
-
- // GetReceiverInfo reads receiver information out of the receiver. Unlike GetOptions
- // and GetRuntimeConfig, this reads the values straight out of memory.
- func GetReceiverInfo(f io.ReadSeeker) (*ReceiverInfo, error) {
-
- info := &ReceiverInfo{}
-
- // Read the receiver-specific configuration.
- // 0x80600 and 0x22d both contain the same data, but for some reason some
- // code reads from one area and some from the other.
- b, err := ReadMemory(f, 0x80600, 0x97)
- if err != nil {
- return nil, err
- }
- info.OptionMemory = b[0:60]
- info.Name1 = cStrtoGoStr(b[60:96])
- info.Name2 = cStrtoGoStr(b[96:132])
- info.SerialNumberString = cStrtoGoStr(b[132:147])
- info.SerialNumber = int(binary.BigEndian.Uint32(b[147:151]))
-
- configBase := uint32(0x000001F0)
-
- // read the configuration memory
- b, err = ReadMemory(f, configBase, 0x3A)
- if err != nil {
- return nil, err
- }
- info.ConfigMemory = b
-
- date := int(binary.BigEndian.Uint32(info.ConfigMemory[0x222-configBase:]))
- month := date / 10000
- day := date / 100 % 100
- year := date % 100
- if year >= 80 {
- year += 1900
- } else {
- year += 2000
- }
- info.FirmwareDate = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
-
- ver := int(binary.BigEndian.Uint16(info.ConfigMemory[0x204-configBase:]))
- info.FirmwareVersionMajor = ver / 100
- info.FirmwareVersionMinor = ver % 100
- info.FirmwareVersion = float64(ver) / 100
-
- info.Code1Start = binary.BigEndian.Uint32(info.ConfigMemory[0x1f0-configBase:])
- info.Code1End = binary.BigEndian.Uint32(info.ConfigMemory[0x1f4-configBase:])
- info.Code2Start = binary.BigEndian.Uint32(info.ConfigMemory[0x1f8-configBase:])
- info.Code2End = binary.BigEndian.Uint32(info.ConfigMemory[0x1fc-configBase:])
-
- // read checksums
- b, err = ReadMemory(f, info.Code1End-12, 4)
- if err != nil {
- return nil, err
- }
- info.Code1Checksum = binary.BigEndian.Uint32(b)
- b, err = ReadMemory(f, info.Code2End-12, 4)
- if err != nil {
- return nil, err
- }
- info.Code2Checksum = binary.BigEndian.Uint32(b)
-
- if info.FirmwareVersion > 5.60 {
- }
- return info, nil
- }
-
- func CaptureReceiverFirmware(f io.ReadSeeker) (*dotx.DotXFile, error) {
-
- info, err := GetReceiverInfo(f)
- if err != nil {
- return nil, err
- }
- dotX := dotx.NewDotX()
-
- // these chip selects are hard-coded into the first two records in the file
- dotX.AddBlock(0x00FFFA54, []byte{0x70, 0x07, 0x78, 0x71})
- dotX.AddBlock(0x00FFFA46, []byte{0x03, 0xF5})
-
- // then the code blocks
- codeBlocks := []*dotx.Block{
- {StartAddr: info.Code1Start, EndAddr: info.Code1End},
- {StartAddr: info.Code2Start, EndAddr: info.Code2End},
- }
-
- totalBytes := 0
-
- for _, cb := range codeBlocks {
- totalBytes += cb.Len()
- }
-
- bytesWriten := 0
-
- for _, block := range codeBlocks {
- blockLen := block.Len()
- b, err := ReadMemory(f, block.StartAddr, blockLen)
- if err != nil {
- return nil, err
- }
- block.Bytes = b
- dotX.Blocks = append(dotX.Blocks, block)
- bytesWriten += blockLen
- }
-
- return dotX, nil
- }
|