// ============================================================
// antenna_mount.scad — LTE + GNSS Antenna Brackets  Rev A
// 2026-03-01  sl-mechanical
// ============================================================
// Stem-mounted brackets for the SIM7600X cellular/GPS system.
//
//   lte_bracket()      25 mm stem clamp + arm with 2× SMA
//                      bulkhead holes (LTE main + diversity).
//                      Antennas point skyward.
//
//   gnss_platform()    25 mm stem clamp + upward-facing tray
//                      for active GNSS patch antenna (≤40×40 mm).
//
// Recommended stem positions (above base plate):
//   LTE bracket        500–600 mm (above battery carousel)
//   GNSS platform      750–800 mm (below sensor head, clear sky)
//
// Both use the same split-collar design:
//   M4 clamping bolts + M4 set screw (height lock).
//   Cable-tie slot on rear half for u.FL pigtail management.
//
// u.FL → SMA pigtail cables route down stem to SIM7600X HAT.
//
// ⚠  VERIFY:  MAWB_HOLE_X / MAWB_HOLE_Y for any M2.5 pattern
//             SMA_D for your SMA bulkhead thread OD
//
// RENDER options:
//   "lte_assembly"    LTE bracket view (default)
//   "lte_front"       LTE collar front half for slicing
//   "lte_rear"        LTE collar rear half
//   "lte_arm"         LTE SMA arm for slicing (print flat)
//   "gnss_assembly"   GNSS platform view
//   "gnss_front"      GNSS collar front half
//   "gnss_rear"       GNSS collar rear half
//   "gnss_tray"       GNSS patch-antenna tray for slicing
//   "full_stem"       both brackets on 400 mm stem stub
// ============================================================

RENDER = "lte_assembly";

// ── Stem ─────────────────────────────────────────────────────
STEM_OD   = 25.0;
STEM_BORE = 25.4;   // +0.4 clearance

// ── Collar (shared) ──────────────────────────────────────────
COL_OD      = 52.0;
COL_H       = 28.0;
COL_BOLT_X  = 19.0;   // M4 clamping bolt CL from stem axis
COL_BOLT_D  =  4.5;   // M4 clearance hole
COL_NUT_W   =  7.0;   // M4 hex nut A/F
COL_NUT_H   =  3.4;

// Cable-tie slot on rear half outer face (for pigtail routing)
TIE_W       =  5.0;
TIE_D       =  3.0;
TIE_Z1      = COL_H * 0.35;
TIE_Z2      = COL_H * 0.70;

// ── LTE SMA arm ──────────────────────────────────────────────
// 2× SMA bulkhead connectors pointing skyward
SMA_D       =  6.6;   // SMA bulkhead clearance hole (6.35 mm thread)
SMA_NUT_AF  = 10.2;   // SMA hex-nut capture across-flats
SMA_NUT_H   =  4.5;   // hex-nut pocket depth (bottom of arm)
SMA_SPACING = 22.0;   // centre-to-centre between 2 SMA positions
LTE_ARM_L   = 40.0;   // arm length (from collar OD to SMA CL)
LTE_ARM_W   = SMA_SPACING + 18.0;  // arm width
LTE_ARM_H   =  9.0;   // arm thickness
LTE_SMA_Y   = LTE_ARM_L * 0.65;    // SMA position along arm

// Pigtail cable relief (semi-circular groove on arm underside)
PIGTAIL_D   =  4.5;

// M3 attachment bolts (arm → collar boss)
M3_D        =  3.3;

// ── GNSS patch-antenna tray ───────────────────────────────────
GNSS_PATCH  = 40.0;   // maximum patch antenna side (fits 25, 35, 40 mm)
GNSS_LIP_T  =  2.2;   // lip wall thickness
GNSS_LIP_H  =  3.0;   // lip height above tray surface
GNSS_TRAY_T =  3.0;   // tray base thickness

// Optional M2 bolt-down pattern for larger patch antennas
GNSS_M2_SP  = 30.0;   // M2 spacing (verify with your patch antenna)
M2_D        =  2.2;

// Coax cable slot (centre of tray, through base)
GNSS_COAX_W =  5.5;

// Arm connecting tray to collar
GNSS_ARM_L  = 28.0;
GNSS_ARM_W  = 22.0;
GNSS_ARM_H  =  7.0;

// Spacing between LTE and GNSS collars on stem
STEM_SPACING = 80.0;

$fn = 64;
e   = 0.01;

// ─────────────────────────────────────────────────────────────
// collar_half(side, arm_type)
//   arm_type: "lte" | "gnss" | "none"
//   Print flat-face-down.
// ─────────────────────────────────────────────────────────────
module collar_half(side="front", arm_type="lte") {
    y_front = (side == "front");
    has_arm = y_front && (arm_type != "none");

    arm_w = (arm_type == "lte") ? LTE_ARM_W : GNSS_ARM_W;
    arm_l = (arm_type == "lte") ? LTE_ARM_L : GNSS_ARM_L;
    arm_h = (arm_type == "lte") ? LTE_ARM_H : GNSS_ARM_H;
    arm_z = COL_H/2 - arm_h/2;

    difference() {
        union() {
            // D-shaped collar half
            intersection() {
                cylinder(d=COL_OD, h=COL_H);
                translate([-COL_OD/2, y_front ? 0 : -COL_OD/2, 0])
                    cube([COL_OD, COL_OD/2, COL_H]);
            }

            // Arm boss integrated into front half
            if (has_arm)
                translate([-arm_w/2, COL_OD/2, arm_z])
                    cube([arm_w, arm_l, arm_h]);
        }

        // Stem bore
        translate([0, 0, -e])
            cylinder(d=STEM_BORE, h=COL_H + 2*e);

        // M4 clamping bolt holes (Y direction)
        for (bx=[-COL_BOLT_X, COL_BOLT_X])
            translate([bx, y_front ? COL_OD/2 : 0, COL_H/2])
                rotate([90,0,0])
                    cylinder(d=COL_BOLT_D, h=COL_OD/2 + e);

        // M4 hex nut pockets (rear half only)
        if (!y_front)
            for (bx=[-COL_BOLT_X, COL_BOLT_X])
                translate([bx, -(COL_OD/4 + e), COL_H/2])
                    rotate([90,0,0])
                        cylinder(d=COL_NUT_W/cos(30), h=COL_NUT_H+e,
                                 $fn=6);

        // M4 set screw (height lock, front half outer face)
        if (y_front)
            translate([0, COL_OD/2, COL_H * 0.75])
                rotate([90,0,0])
                    cylinder(d=COL_BOLT_D,
                             h=COL_OD/2 - STEM_BORE/2 + e);

        // Cable-tie grooves on rear half outer surface (2×)
        if (!y_front)
            for (tz=[TIE_Z1, TIE_Z2])
                translate([-COL_OD/2 - e, -TIE_W/2, tz])
                    cube([TIE_D + e, TIE_W, TIE_W]);

        // M3 attachment holes through arm boss (2×)
        if (has_arm)
            for (dx=[-arm_w/4, arm_w/4])
                translate([dx, COL_OD/2 + arm_l * 0.45, arm_z - e])
                    cylinder(d=M3_D, h=arm_h + 2*e);
    }
}

// ─────────────────────────────────────────────────────────────
// lte_sma_arm()
//   Separate arm piece bolts to collar front boss.
//   2× SMA bulkheads point upward.  Pigtail grooves on underside.
//   Print: lay flat on bottom face.
// ─────────────────────────────────────────────────────────────
module lte_sma_arm() {
    difference() {
        translate([-LTE_ARM_W/2, 0, 0])
            cube([LTE_ARM_W, LTE_ARM_L, LTE_ARM_H]);

        // 2× SMA bulkhead through-holes (vertical)
        for (dx=[-SMA_SPACING/2, SMA_SPACING/2]) {
            translate([dx, LTE_SMA_Y, -e])
                cylinder(d=SMA_D, h=LTE_ARM_H + 2*e);
            // Hex-nut pocket from bottom face
            translate([dx, LTE_SMA_Y, -e])
                cylinder(d=SMA_NUT_AF/cos(30), h=SMA_NUT_H + e,
                         $fn=6);
        }

        // u.FL pigtail relief grooves on underside
        for (dx=[-SMA_SPACING/2, SMA_SPACING/2])
            translate([dx, 0, -e])
                rotate([0, 0, 0])
                    linear_extrude(PIGTAIL_D/2 + e)
                        translate([0, LTE_ARM_L/2])
                            circle(d=PIGTAIL_D);

        // M3 attachment holes (collar boss)
        for (dx=[-LTE_ARM_W/4, LTE_ARM_W/4])
            translate([dx, LTE_ARM_L * 0.45, -e])
                cylinder(d=M3_D, h=LTE_ARM_H + 2*e);
    }
}

// ─────────────────────────────────────────────────────────────
// gnss_tray()
//   Horizontal tray faces skyward.  Retention lip on all 4 sides.
//   Central coax slot + optional M2 bolt holes.
//   Print: top face on bed (tray upside-down → no supports needed).
// ─────────────────────────────────────────────────────────────
module gnss_tray() {
    outer = GNSS_PATCH + 2 * GNSS_LIP_T;

    difference() {
        union() {
            // Base plate
            translate([-outer/2, 0, 0])
                cube([outer, outer, GNSS_TRAY_T]);
            // Retention lip (4 walls)
            translate([-outer/2, 0, GNSS_TRAY_T])
                difference() {
                    cube([outer, outer, GNSS_LIP_H]);
                    translate([GNSS_LIP_T, GNSS_LIP_T, -e])
                        cube([GNSS_PATCH, GNSS_PATCH, GNSS_LIP_H + 2*e]);
                }
            // Arm connecting to collar
            translate([-GNSS_ARM_W/2, -GNSS_ARM_L, 0])
                cube([GNSS_ARM_W, GNSS_ARM_L, GNSS_ARM_H]);
        }

        // GNSS coax cable slot (centre, through base)
        translate([-GNSS_COAX_W/2, outer/2 - GNSS_COAX_W/2, -e])
            cube([GNSS_COAX_W, GNSS_COAX_W, GNSS_TRAY_T + 2*e]);

        // M2 bolt-down holes (30×30 mm pattern, centred in tray)
        tray_cx = 0;
        tray_cy = outer/2;
        for (dx=[-GNSS_M2_SP/2, GNSS_M2_SP/2])
            for (dy=[-GNSS_M2_SP/2, GNSS_M2_SP/2])
                translate([tray_cx + dx, tray_cy + dy, -e])
                    cylinder(d=M2_D, h=GNSS_TRAY_T + 2*e);

        // M3 bolt holes (arm → collar)
        for (dx=[-GNSS_ARM_W/4, GNSS_ARM_W/4])
            translate([dx, -GNSS_ARM_L * 0.45, -e])
                cylinder(d=M3_D, h=GNSS_ARM_H + 2*e);
    }
}

// ─────────────────────────────────────────────────────────────
// lte_bracket_assembly() / gnss_bracket_assembly()
// ─────────────────────────────────────────────────────────────
module lte_bracket_assembly() {
    color("SteelBlue",      0.9) collar_half("front", "lte");
    color("CornflowerBlue", 0.9) mirror([0,1,0]) collar_half("rear", "none");
    color("LightSteelBlue", 0.85)
        translate([0, COL_OD/2, COL_H/2 - LTE_ARM_H/2])
            lte_sma_arm();
    // Phantom SMA stub antennas
    for (dx=[-SMA_SPACING/2, SMA_SPACING/2])
        color("DimGray", 0.5)
            translate([dx, COL_OD/2 + LTE_SMA_Y,
                       COL_H/2 + LTE_ARM_H/2])
                cylinder(d=7, h=60);
}

module gnss_bracket_assembly() {
    color("Teal",     0.9) collar_half("front", "gnss");
    color("DarkCyan", 0.9) mirror([0,1,0]) collar_half("rear", "none");
    // Tray: arm at Y−, tray faces +Z
    color("LightCyan", 0.85)
        translate([0, COL_OD/2 + GNSS_ARM_L,
                   COL_H/2 - GNSS_ARM_H/2])
            rotate([90, 0, 0])
                gnss_tray();
    // Phantom GNSS patch
    color("Gold", 0.35)
        translate([-GNSS_PATCH/2,
                   COL_OD/2 + GNSS_ARM_L + GNSS_LIP_T,
                   COL_H/2 + GNSS_ARM_H/2 + GNSS_TRAY_T])
            cube([GNSS_PATCH, GNSS_PATCH, 8]);
}

// ─────────────────────────────────────────────────────────────
// Render selector
// ─────────────────────────────────────────────────────────────
if (RENDER == "lte_assembly") {
    lte_bracket_assembly();
} else if (RENDER == "lte_front") {
    collar_half("front", "lte");
} else if (RENDER == "lte_rear") {
    collar_half("rear", "none");
} else if (RENDER == "lte_arm") {
    translate([0, 0, LTE_ARM_H]) rotate([180,0,0]) lte_sma_arm();
} else if (RENDER == "gnss_assembly") {
    gnss_bracket_assembly();
} else if (RENDER == "gnss_front") {
    collar_half("front", "gnss");
} else if (RENDER == "gnss_rear") {
    collar_half("rear", "none");
} else if (RENDER == "gnss_tray") {
    gnss_tray();
} else if (RENDER == "full_stem") {
    color("Silver", 0.2)
        translate([0,0,-40])
            cylinder(d=STEM_OD, h=STEM_SPACING + COL_H + 80);
    lte_bracket_assembly();
    translate([0, 0, STEM_SPACING])
        gnss_bracket_assembly();
}
