Fixed code smells
Some checks failed
Run tests / test (1.25) (push) Failing after 15s
Run tests / test (stable) (push) Failing after 17s

This commit is contained in:
2026-02-22 21:14:58 +01:00
parent 3bcbaf2135
commit 32f6c38c13
17 changed files with 1593 additions and 22 deletions

View File

@@ -0,0 +1,158 @@
package fields
// SurveillanceStatus is the surveillance status
//
// Specified in Doc 9871 / A.2.3.2.6
type SurveillanceStatus byte
const (
SSNoCondition SurveillanceStatus = iota // No aircraft category information
SSPermanentAlert // Permanent alert (emergency condition)
SSTemporaryAlert // Temporary alert (change in Mode A identity code other than emergency condition)
SSSPICondition // SPI condition
)
// AltitudeSource is the type of source of the Altitude: Barometric or GNSS.
type AltitudeSource byte
const (
AltitudeBarometric AltitudeSource = iota // Altitude is barometric altitude
AltitudeGNSS // Altitude is GNSS height (HAE)
)
// AltitudeReportMethod defines how the altitude is reported.
type AltitudeReportMethod byte
const (
AltitudeReport100FootIncrements AltitudeReportMethod = iota // 100-foot increments
AltitudeReport25FootIncrements // 25-foot increments
)
// CompactPositionReportingFormat is the CPR (Compact Position Reporting) format definition
//
// Specified in Doc 9871 / A.2.3.3.3
type CompactPositionReportingFormat byte
const (
CPRFormatEven CompactPositionReportingFormat = iota // Even format coding
CPRFormatOdd // Odd format coding
)
func ParseCompactPositioningReportFormat(data []byte) CompactPositionReportingFormat {
bits := (data[2] & 0x04) >> 2
return CompactPositionReportingFormat(bits)
}
func ParseAltitude(data []byte) (altitude int32, source AltitudeSource, method AltitudeReportMethod, err error) {
source = AltitudeBarometric
if format := (data[0] & 0xF8) >> 3; 20 <= format && format <= 22 {
source = AltitudeGNSS
}
// Altitude code is a 12 bits fields, so read a uint16
// bit | 8 9 10 11 12 13 14 15| 16 17 18 19 20 21 22 23 |
// message | x x x x x x x x| x x x x _ _ _ _ |
// 100 foot |C1 A1 C2 A2 C4 A4 B1 Q| B2 D2 B4 D4 _ _ _ _ |
// Get the Q bit
if qBit := (data[1] & 0x01) != 0; qBit {
// If the Q bit equals 1, the 11-bit field represented by bits 8 to 14 and 16 to 18
n := uint16(0)
n |= uint16(data[1]&0xFE) << 3
n |= uint16(data[2]&0xF0) >> 4
return 25*int32(n) - 1000, source, AltitudeReport25FootIncrements, nil
}
// Otherwise, altitude is reported in 100 foot increments
c1 := (data[1] & 0x80) != 0
a1 := (data[1] & 0x40) != 0
c2 := (data[1] & 0x20) != 0
a2 := (data[1] & 0x10) != 0
c4 := (data[1] & 0x08) != 0
a4 := (data[1] & 0x04) != 0
b1 := (data[1] & 0x02) != 0
b2 := (data[2] & 0x80) != 0
d2 := (data[2] & 0x40) != 0
b4 := (data[2] & 0x20) != 0
d4 := (data[2] & 0x10) != 0
if altitude, err = GillhamToAltitude(false, d2, d4, a1, a2, a4, b1, b2, b4, c1, c2, c4); err != nil {
return
}
return
}
// MovementStatus is the status of the Movement information
type MovementStatus int
const (
// MSNoInformation indicates no information
MSNoInformation MovementStatus = 0 // No information
MSAircraftStopped MovementStatus = 1 // The aircraft is stopped
MSAboveMaximum MovementStatus = 124 // The Movement is above the maximum
MSReservedDecelerating MovementStatus = 125 // Reserved
MSReservedAccelerating MovementStatus = 126 // Reserved
MSReservedBackingUp MovementStatus = 127 // Reserved
)
func ParseMovementStatus(data []byte) (float64, MovementStatus) {
bits := (data[0]&0x07)<<4 + (data[1]&0xF0)>>4
status := MovementStatus(bits)
return status.Speed(), status
}
// Speed in knots.
func (status MovementStatus) Speed() float64 {
if status == 0 || status == 1 || status > 124 {
return 0
} else if 2 <= status && status <= 8 {
return 0.125 + float64(status-2)*0.125
} else if 9 <= status && status <= 12 {
return 1 + float64(status-9)*0.25
} else if 13 <= status && status <= 38 {
return 2 + float64(status-13)*0.5
} else if 39 <= status && status <= 93 {
return 15 + float64(status-39)*1.0
} else if 94 <= status && status <= 108 {
return 70 + float64(status-94)*2.0
} else if 109 <= status && status <= 123 {
return 100 + float64(status-109)*5.0
}
// Movement max
return 175
}
// EncodedLatitude is the encoded latitude
//
// Specified in Doc 9871 / C.2.6
type EncodedLatitude uint32
func ParseEncodedLatitude(data []byte) EncodedLatitude {
return EncodedLatitude((data[2]&0x02)>>1)<<16 |
EncodedLatitude((data[2]&0x01)<<7+(data[3]&0xFE)>>1)<<8 |
EncodedLatitude((data[3]&0x01)<<7+(data[4]&0xFE)>>1)
}
// EncodedLongitude is the encoded longitude
//
// Specified in Doc 9871 / C.2.6
type EncodedLongitude uint32
func ParseEncodedLongitude(data []byte) EncodedLongitude {
return EncodedLongitude(data[4]&0x01)<<16 |
EncodedLongitude(data[5])<<8 |
EncodedLongitude(data[6])
}
func ParseGroundTrackStatus(data []byte) (track float64, status bool) {
status = (data[1] & 0x08) != 0
allBits := (uint16(data[1]&0x07)<<8 | uint16(data[2]&0xF0)) >> 4
track = float64(allBits) * 360.0 / 128.0
return
}
func ParseTimeSynchronizedToUTC(data []byte) bool {
return ((data[2] & 0x08) >> 3) != 0
}

View File

@@ -0,0 +1,208 @@
package fields
// NumericValueStatus is the status of the numeric values, such as air speed, velocity, etc.
type NumericValueStatus byte
const (
NVSNoInformation NumericValueStatus = iota // No velocity information
NVSRegular // Velocity is computed on the linear scale value of field * factor
NVSMaximum // Velocity field value indicates velocity greater the maximum of the scale
)
// ParseHeightDifference reads the Height difference from a 56 bits data field
func ParseHeightDifference(data []byte) (int16, NumericValueStatus) {
negative := data[6]&0x80 != 0
difference := int16(data[6] & 0x7F)
if difference == 0 {
return 0, NVSNoInformation
} else if difference >= 127 {
if negative {
return -3150, NVSMaximum
} else {
return 3150, NVSMaximum
}
}
difference = (difference - 1) * 25
if negative {
difference = -difference
}
return difference, NVSRegular
}
// VerticalRateSource is the Source Bit for Vertical Rate definition
//
// Specified in Doc 9871 / Table A-2-9
type VerticalRateSource byte
const (
VerticalRateSourceGNSS VerticalRateSource = iota // GNSS source
VerticalRateSourceBarometric // Barometric source
)
// DirectionNorthSouth is the Direction Bit NS Velocity definition
//
// Specified in Doc 9871 / Table A-2-9
type DirectionNorthSouth byte
func ParseDirectionNorthSouth(data []byte) DirectionNorthSouth {
return DirectionNorthSouth((data[3] & 0x80) >> 7)
}
func ParseVelocityNorthSouthNormal(data []byte) (velocity uint16, status NumericValueStatus) {
velocity = (uint16(data[3]&0x7F)<<8 | uint16(data[4]&0xE0)) >> 5
if velocity == 0 {
return 0, NVSNoInformation
} else if velocity >= 1023 {
return 1023, NVSMaximum
}
return velocity - 1, NVSRegular
}
func ParseVelocityNorthSouthSupersonic(data []byte) (velocity uint16, status NumericValueStatus) {
velocity = (uint16(data[3]&0x7F)<<8 | uint16(data[4]&0xE0)) >> 5
if velocity == 0 {
return 0, NVSNoInformation
} else if velocity >= 1023 {
return 4088, NVSMaximum
}
return (velocity - 1) * 4, NVSRegular
}
const (
DNSNorth DirectionNorthSouth = iota // North
DNSSouth // South
)
// DirectionEastWest is the Direction Bit EW Velocity definition
//
// Specified in Doc 9871 / Table A-2-9
type DirectionEastWest byte
func ParseDirectionEastWest(data []byte) DirectionEastWest {
return DirectionEastWest((data[1] & 0x04) >> 2)
}
func ParseVelocityEastWestNormal(data []byte) (velocity uint16, status NumericValueStatus) {
velocity = (uint16(data[1]&0x03) | uint16(data[2]))
if velocity == 0 {
return 0, NVSNoInformation
} else if velocity >= 1023 {
return 1023, NVSMaximum
}
return velocity - 1, NVSRegular
}
func ParseVelocityEastWestSupersonic(data []byte) (velocity uint16, status NumericValueStatus) {
velocity = (uint16(data[1]&0x03) | uint16(data[2]))
if velocity == 0 {
return 0, NVSNoInformation
} else if velocity >= 1023 {
return 4088, NVSMaximum
}
return (velocity - 1) * 4, NVSRegular
}
const (
DEWEast DirectionEastWest = iota // East
DEWWest // West
)
func ParseIntentChange(data []byte) bool {
return (data[1]&0x80)>>7 != 0
}
func ParseIFRCapability(data []byte) bool {
return (data[1]&0x40)>>6 != 0
}
// NavigationUncertaintyCategory is the Navigation Uncertainty Category definition
//
// Specified in Doc 9871 / Table A-2-9
type NavigationUncertaintyCategory byte
const (
NUCPUnknown NavigationUncertaintyCategory = iota // Unknown
NUCPHorizontalLowerThan10VerticalLowerThan15Point2 // Horizontal < 10m/s and Vertical < 15.2m/s
NUCPHorizontalLowerThan3VerticalLowerThan4Point6 // Horizontal < 3m/s and Vertical < 4.6m/s
NUCPHorizontalLowerThan1VerticalLowerThan1Point5 // Horizontal < 1m/s and Vertical < 1.5m/s
NUCPHorizontalLowerThan0Point3VerticalLowerThan0Point46 // Horizontal < 0.3m/s and Vertical < 0.46m/s
)
func ParseeNavigationUncertaintyCategory(data []byte) NavigationUncertaintyCategory {
return NavigationUncertaintyCategory((data[1] & 0x38) >> 3)
}
func ParseMagneticHeading(data []byte) (heading float64, status bool) {
status = (data[1]&0x04)>>2 != 0
value := uint16(data[1]&0x03)<<8 | uint16(data[2])
heading = float64(value) * 360 / 1024.0
return
}
func ParseAirspeedNormal(data []byte) (speed uint16, status NumericValueStatus) {
velocity := (uint16(data[3]&0x7F)<<8 | uint16(data[4]&0xE0)) >> 5
if velocity == 0 {
return 0, NVSNoInformation
} else if velocity >= 1023 {
return 1023, NVSMaximum
}
return velocity - 1, NVSRegular
}
func ParseAirspeedSupersonic(data []byte) (speed uint16, status NumericValueStatus) {
velocity := (uint16(data[3]&0x7F)<<8 | uint16(data[4]&0xE0)) >> 5
if velocity == 0 {
return 0, NVSNoInformation
} else if velocity >= 1023 {
return 4088, NVSMaximum
}
return (velocity - 1) * 4, NVSRegular
}
func ParseVerticalRateSource(data []byte) VerticalRateSource {
return VerticalRateSource((data[4] & 0x10) >> 4)
}
func ParseVerticalRate(data []byte) (rate int16, status NumericValueStatus) {
negative := data[4]&0x08 != 0
rate = int16(uint16(data[4]&0x07)<<8 | uint16(data[5]&0xFC))
if rate == 0 {
return 0, NVSNoInformation
} else if rate >= 511 {
if negative {
return -32640, NVSMaximum
}
return 32640, NVSMaximum
}
rate = (rate - 1) * 64
if negative {
rate = -rate
}
return rate, NVSRegular
}
func ParseHeightDifferenceFromBaro(data []byte) (difference int16, status NumericValueStatus) {
negative := data[6]&0x80 != 0
difference = int16(data[6] & 0x7F)
if difference == 0 {
return 0, NVSNoInformation
} else if difference >= 127 {
if negative {
return -3150, NVSMaximum
} else {
return 3150, NVSMaximum
}
}
difference = (difference - 1) * 25
if negative {
difference = -difference
}
return difference, NVSRegular
}

View File

@@ -0,0 +1,21 @@
package fields
// EmergencyPriorityStatus is the Emergency Priority Status definition
//
// Specified in Doc 9871 / Table B-2-97a
type EmergencyPriorityStatus byte
const (
EPSNoEmergency EmergencyPriorityStatus = iota // No emergency
EPSGeneralEmergency // General emergency
EPSLifeguardMedical // Lifeguard/medical emergency
EPSMinimumFuel // Minimum fuel
EPSNoCommunication // No communications
EPSUnlawfulInterference // Unlawful interference
EPSDownedAircraft // Downed aircraft
EPSReserved7 // Reserved
)
func ParseEmergencyPriorityStatus(data []byte) EmergencyPriorityStatus {
return EmergencyPriorityStatus((data[1] & 0xE0) >> 5)
}

View File

@@ -0,0 +1,176 @@
package fields
// VerticalDataAvailableSourceIndicator is the Vertical Data Available / Source Indicator definition
//
// Specified in Doc 9871 / B.2.3.9.3
type VerticalDataAvailableSourceIndicator byte
const (
VDANoValidDataAvailable VerticalDataAvailableSourceIndicator = iota // No data available
VDAAutopilot // Autopilot control panel selected value, such as Mode Control Panel (MCP) or Flight Control Unit (FCU)
VDAHoldingAltitude // Holding altitude
VDAFMS // FMS/RNAV system
)
func ParseVerticalDataAvailableSourceIndicator(data []byte) VerticalDataAvailableSourceIndicator {
return VerticalDataAvailableSourceIndicator((data[0]&0x01)<<1 + (data[1]&0x80)>>7)
}
// TargetAltitudeType is the Target Altitude Type definition
//
// Specified in Doc 9871 / B.2.3.9.4
type TargetAltitudeType byte
const (
TATReferencedToPressureAltitude TargetAltitudeType = iota // Referenced to pressure-altitude (flight level)
TATReferencedToBarometricAltitude // Referenced to barometric corrected altitude (mean sea level)
)
// ReadTargetAltitudeType reads the TargetAltitudeType from a 56 bits data field
func ParseTargetAltitudeType(data []byte) TargetAltitudeType {
return TargetAltitudeType((data[1] & 0x40) >> 6)
}
// TargetAltitudeCapability is the Target Altitude Capability definition
//
// Specified in Doc 9871 / B.2.3.9.5
type TargetAltitudeCapability byte
const (
TACAltitudeOnly TargetAltitudeCapability = iota // Holding altitude only
TACAltitudeOrAutopilot // Either holding altitude or autopilot control panel selected altitude
TACAltitudeOrAutopilotOrFMS // Either holding altitude, autopilot control panel selected altitude, or any FMS/RNAV level-off altitude
TACReserved3 // Reserved
)
// ParseTargetAltitudeCapability reads the TargetAltitudeCapability from a 56 bits data field
func ParseTargetAltitudeCapability(data []byte) TargetAltitudeCapability {
return TargetAltitudeCapability((data[1] & 0x18) >> 3)
}
// VerticalModeIndicator is the Vertical Mode Indicator definition
//
// Specified in Doc 9871 / B.2.3.9.6
type VerticalModeIndicator byte
const (
VMIUnknown VerticalModeIndicator = iota // Unknown mode or information unavailable
VMIAcquiringMode // Acquiring Mode
VMICapturingMode // Capturing or Maintaining Mode
VMIReserved3 // Reserved
)
// ParseVerticalModeIndicator reads the VerticalModeIndicator from a 56 bits data field
func ParseVerticalModeIndicator(data []byte) VerticalModeIndicator {
return VerticalModeIndicator((data[1] & 0x06) >> 1)
}
// HorizontalDataAvailableSourceIndicator is the Horizontal Data Available / Source Indicator definition
//
// Specified in Doc 9871 / B.2.3.9.8
type HorizontalDataAvailableSourceIndicator byte
const (
HDANoValidDataAvailable HorizontalDataAvailableSourceIndicator = iota // No data available
HDAAutopilot // Autopilot control panel selected value, such as Mode Control Panel (MCP) or Flight Control Unit (FCU)
HDAHoldingAltitude // Maintaining current heading or track angle (e.g. autopilot mode select)
HDAFMS // FMS/RNAV system (indicates track angle specified by leg type)
)
// ParseHorizontalDataAvailableSourceIndicator reads the HorizontalDataAvailableSourceIndicator from a 56 bits data field
func ParseHorizontalDataAvailableSourceIndicator(data []byte) HorizontalDataAvailableSourceIndicator {
return HorizontalDataAvailableSourceIndicator((data[3] & 0x60) >> 5)
}
// TargetHeadingTrackIndicator is the Target Heading / Track Angle Indicator definition
//
// Specified in Doc 9871 / B.2.3.9.10
type TargetHeadingTrackIndicator byte
const (
TargetTrackHeadingAngle TargetHeadingTrackIndicator = iota // Target heading angle is being reported
TargetTrackAngle // Track angle is being reported
)
// ParseTargetHeadingTrackIndicator reads the TargetHeadingTrackIndicator from a 56 bits data field
func ParseTargetHeadingTrackIndicator(data []byte) TargetHeadingTrackIndicator {
return TargetHeadingTrackIndicator((data[4] & 0x08) >> 3)
}
// ReadTargetHeadingTrackAngle reads the TargetAltitude from a 56 bits data field
// Specified in Doc 9871 / B.2.3.9.9
func ReadTargetHeadingTrackAngle(data []byte) (uint16, NumericValueStatus) {
heading := (uint16(data[3]&0x1F)<<8 | uint16(data[2]&0xF0)) >> 4
if heading > 359 {
return 0, NVSMaximum
}
return heading, NVSRegular
}
// HorizontalModeIndicator is the Horizontal Mode Indicator definition
//
// Specified in Doc 9871 / B.2.3.9.11
type HorizontalModeIndicator byte
const (
HMIUnknown HorizontalModeIndicator = iota // Unknown mode or information unavailable
HMIAcquiringMode // Acquiring Mode
HMICapturingMode // Capturing or Maintaining Mode
HMIReserved3 // Reserved
)
// ParseHorizontalModeIndicator reads the HorizontalModeIndicator from a 56 bits data field
func ParseHorizontalModeIndicator(data []byte) HorizontalModeIndicator {
return HorizontalModeIndicator((data[4] & 0x06) >> 1)
}
// NavigationalAccuracyCategoryPositionV1 is the Navigational Accuracy Category Position definition
//
// Specified in Doc 9871 / B.2.3.9.12
type NavigationalAccuracyCategoryPositionV1 byte
const (
NACPV1EPUGreaterThan18Point52Km NavigationalAccuracyCategoryPositionV1 = iota // EPU >= 18.52 km (10 NM) - Unknown accuracy
NACPV1EPULowerThan18Point52Km // EPU < 18.52 km (10 NM) - RNP-10 accuracy
NACPV1EPULowerThan7Point408Km // EPU < 7.408 km (4 NM) - RNP-4 accuracy
NACPV1EPULowerThan3Point704Km // EPU < 3.704 km (2 NM) - RNP-2 accuracy
NACPV1EPUGreaterThan1852M // EPU < 1 852 m (1 NM) - RNP-1 accuracy
NACPV1EPULowerThan926M // EPU < 926 m (0.5 NM) - RNP-0.5 accuracy
NACPV1EPUGreaterThan555Point6M // EPU < 555.6 m ( 0.3 NM) - RNP-0.3 accuracy
NACPV1EPULowerThan185Point2M // EPU < 185.2 m (0.1 NM) - RNP-0.1 accuracy
NACPV1EPUGreaterThan92Point6M // EPU < 92.6 m (0.05 NM) - e.g. GPS (with SA)
NACPV1EPULowerThan30MAndVEPULowerThan45M // EPU < 30 m and VEPU < 45 m - e.g. GPS (SA off)
NACPV1EPULowerThan10MAndVEPULowerThan15M // EPU < 10 m and VEPU < 15 m - e.g. WAAS
NACPV1EPULowerThan4MAndVEPULowerThan3M // EPU < 3 m and VEPU < 4 m - e.g. LAAS
NACPV1Reserved12 // Reserved
NACPV1Reserved13 // Reserved
NACPV1Reserved14 // Reserved
NACPV1Reserved15 // Reserved
)
// ParseNavigationalAccuracyCategoryPositionV1 reads the NavigationalAccuracyCategoryPositionV1 from a 56 bits data field
func ParseNavigationalAccuracyCategoryPositionV1(data []byte) NavigationalAccuracyCategoryPositionV1 {
return NavigationalAccuracyCategoryPositionV1((data[4]&0x01)<<3 + (data[5]&0xE0)>>5)
}
// NICBaro is the NIC Baro definition
//
// Specified in Doc 9871 / B.2.3.9.13
type NICBaro byte
const (
// NICBGilhamNotCrossChecked indicates that the barometric altitude that is being reported in the Airborne
// Position Message is based on a Gilham coded input that has not been cross-checked against another source of
// pressure-altitude
NICBGilhamNotCrossChecked NICBaro = iota
// NICBGilhamCrossCheckedOrNonGilham indicates that the barometric altitude that is being reported in the Airborne
// Position Message is either based on a Gilham code input that has been cross-checked against another source of
// pressure-altitude and verified as being consistent, or is based on a non-Gilham coded source
NICBGilhamCrossCheckedOrNonGilham
)
// ParseNICBaro reads the NICBaro from a 56 bits data field
func ParseNICBaro(data []byte) NICBaro {
return NICBaro((data[5] & 0x10) >> 4)
}

View File

@@ -0,0 +1,78 @@
package fields
import "errors"
// GillhamToAltitude convert an altitude given in Gillham bits to an altitude in feet.
func GillhamToAltitude(d1, d2, d4, a1, a2, a4, b1, b2, b4, c1, c2, c4 bool) (int32, error) {
fiveHundredBits := uint16(0)
if d1 {
fiveHundredBits |= 0x0100
}
if d2 {
fiveHundredBits |= 0x0080
}
if d4 {
fiveHundredBits |= 0x0040
}
if a1 {
fiveHundredBits |= 0x0020
}
if a2 {
fiveHundredBits |= 0x0010
}
if a4 {
fiveHundredBits |= 0x0008
}
if b1 {
fiveHundredBits |= 0x0004
}
if b2 {
fiveHundredBits |= 0x0002
}
if b4 {
fiveHundredBits |= 0x0001
}
oneHundredBits := uint16(0)
if c1 {
oneHundredBits |= 0x0004
}
if c2 {
oneHundredBits |= 0x0002
}
if c4 {
oneHundredBits |= 0x0001
}
oneHundred := int32(grayToBinary(oneHundredBits))
fiveHundred := int32(grayToBinary(fiveHundredBits))
// Check for invalid codes.
if oneHundred == 5 || oneHundred == 6 || oneHundred == 0 {
return 0, errors.New("the bits C1 to to C4 are incorrect")
}
// Remove 7s from OneHundreds.
if oneHundred == 7 {
oneHundred = 5
}
// Correct order of OneHundreds.
if fiveHundred%2 != 0 {
oneHundred = 6 - oneHundred
}
// Convert to feet and apply altitude datum offset.
return (int32(fiveHundred)*500 + int32(oneHundred)*100) - 1300, nil
}
func grayToBinary(num uint16) uint16 {
temp := uint16(0)
temp = num ^ (num >> 8)
temp ^= temp >> 4
temp ^= temp >> 2
temp ^= temp >> 1
return temp
}

View File

@@ -0,0 +1,52 @@
package fields
import "testing"
func TestGillhamToAltitude(t *testing.T) {
tests := []struct {
Test string
Want int32
}{
{"00000000010", -1000},
{"00000001010", -500},
{"00000011010", 0},
{"00000011110", 100},
{"00000010011", 600},
{"00000110010", 1000},
{"00001001001", 5800},
{"00011100100", 10300},
{"01100011010", 32000},
{"01110000100", 46300},
{"10000000011", 126600},
{"10000000001", 126700},
}
for _, test := range tests {
t.Run(test.Test, func(t *testing.T) {
alt, err := GillhamToAltitude(testString2Bits(test.Test))
if err != nil {
t.Fatalf("%v", err)
}
if alt != test.Want {
t.Errorf("expected altitude %d, got %d", test.Want, alt)
}
})
}
}
func testString2Bits(s string) (bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool) {
d2 := s[0] == '1'
d4 := s[1] == '1'
a1 := s[2] == '1'
a2 := s[3] == '1'
a4 := s[4] == '1'
b1 := s[5] == '1'
b2 := s[6] == '1'
b4 := s[7] == '1'
c1 := s[8] == '1'
c2 := s[9] == '1'
c4 := s[10] == '1'
return false, d2, d4, a1, a2, a4, b1, b2, b4, c1, c2, c4
}